ReentrantLock原始碼分析
ReentrantLock是獨享鎖,同時是基於AQS實現的,因此它內部肯定是通過自定義AQS獨佔模式下的同步器來實現獨享鎖,該同步器需要重寫AQS提供的tryAcquire()和tryRelease()方法,只需要告訴AQS嘗試獲取同步資源和釋放同步資源是否成功即可。
AQS子類需要定義以及維護同步狀態的值,在ReentrantLock中使用state為0表示同步資源沒有被執行緒所持有,使用state不為0表示同步資源已經被執行緒所持有。
ReentrantLock有公平和非公平兩種模式,公平模式是指多執行緒按照申請鎖的順序來獲取鎖,非公平模式是指多執行緒並非按照申請鎖的順序來獲取鎖。
ReentrantLock的結構
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
/**
* 抽象同步器(AQS獨佔模式)
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
// ......
}
/**
* 非公平同步器
*/
static final class NonfairSync extends Sync {
// ......
}
/**
* 公平同步器
*/
static final class FairSync extends Sync {
// ......
}
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public void lock() {
sync.lock();
}
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
sync.release(1);
}
// 其它省略
}
可以發現ReentrantLock中定義了一個抽象同步器(Sync)、非公平同步器(NonfairSync)、公平同步器(FairSync),同時非公平同步器和公平同步器都繼承抽象同步器。
同時ReentrantLock中存在一個全域性的抽象同步器屬性(sync),通過ReentrantLock的構建方法來進行初始化,並通過引數來指定是使用公平同步器還是非公平同步器,預設情況下是使用非公平同步器。
同時ReentrantLock中的核心方法,如lock()加鎖方法,是呼叫抽象同步器宣告的lock()方法,unlock()解鎖方法,是呼叫AQS的release()方法,而tryLock()方法,是呼叫抽象同步器提供的nonfairTryAcquire()方法。
剖析抽象同步器
/**
* 抽象同步器(AQS獨佔模式)
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 宣告瞭lock()方法,用於加鎖
* ReentrantLock的lock()方法會呼叫抽象同步器宣告的lock()方法
*/
abstract void lock();
/**
* 用於嘗試獲取鎖,如果獲取成功則返回true,並修改同步狀態的值,否則返回false
* ReentrantLock的tryLock()方法會呼叫抽象同步器提供的nonfairTryAcquire()方法
*/
final boolean nonfairTryAcquire(int acquires) {
// 獲取當前執行緒
final Thread current = Thread.currentThread();
// 獲取同步狀態
int c = getState();
// 如果同步狀態為0,表示鎖沒有被執行緒所持有
if (c == 0) {
// 通過CAS將同步狀態設定為1,獲取鎖
if (compareAndSetState(0, acquires)) {
// 如果獲取鎖成功,則將擁有同步資源的執行緒設定為當前執行緒
setExclusiveOwnerThread(current);
return true;
}
}
// 否則表示鎖已經被執行緒如果當前執行緒就是擁有鎖的執行緒,它還嘗試獲取鎖,那麼就累加同步狀態的值
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
/**
* 重寫AQS的tryRelease()方法,用於嘗試釋放鎖,如果釋放成功,則返回true,並修改同步狀態的值,否則返回false
*/
protected final boolean tryRelease(int releases) {
// 獲取鎖釋放後剩餘的同步資源
int c = getState() - releases;
// 如果擁有鎖的執行緒並非當前執行緒,則直接丟擲異常(要求誰加的鎖只能由誰進行解鎖)
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 如果鎖釋放後,state的值為0,則返回true,否則返回false(說明執行緒加了不止一把鎖,然後又沒有全釋放)
if (c == 0) {
free = true;
// 清空擁有同步資源的執行緒
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
/**
* 判斷當前執行緒是否持有鎖
*/
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
/**
* 返回Condition例項,在某些場景下可以進行阻塞與喚醒
*/
final ConditionObject newCondition() {
return new ConditionObject();
}
/**
* 獲取擁有鎖的執行緒
*/
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
/**
* 返回當前執行緒擁有鎖的個數
*/
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
/**
* 判斷鎖是否已經被執行緒所持有
*/
final boolean isLocked() {
return getState() != 0;
}
}
抽象同步器宣告的lock()方法用於加鎖,該方法需要由抽象同步器的子類,也就是非公平同步器和公平同步器來實現。
同時抽象同步器中提供了nonfairTryAcquire()方法,該方法會在ReentrantLock的tryLock()方法以及非公平同步器的tryAcquire()方法中被呼叫。
tryAcquire()方法由抽象同步器的子類來重寫,而tryRelease()方法由抽象同步器來重寫。
剖析非公平同步器
/**
* 非公平同步器(繼承抽象同步器)
*/
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 實現抽象同步器宣告的lock()方法
*/
final void lock() {
// 直接通過CAS嘗試獲取鎖,如果獲取成功則將當前執行緒設定為擁有同步資源的執行緒,然後直接返回,否則呼叫AQS的acquire()方法
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
/**
* 重寫AQS的tryAcquire()方法,直接呼叫抽象同步器的nonfairTryAcquire()方法
*/
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
非公平模式下的ReentrantLock的加鎖,將會呼叫非公平同步器的lock()方法,該方法先通過CAS嘗試獲取鎖,當獲取失敗時才會呼叫AQS的acquire()方法,該方法又會呼叫非公平同步器的tryAcquire()方法。
非公平模式下的ReentrantLock的解鎖,將會呼叫AQS的release()方法,該方法又會呼叫抽象同步器提供的tryRelease()方法。
剖析公平同步器
/**
* 公平的同步器(繼承抽象同步器)
*/
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
/**
* 實現抽象同步器宣告的lock()方法
*/
final void lock() {
// 直接呼叫AQS的acquire()方法
acquire(1);
}
/**
* 重寫AQS的tryAcquire()方法
*/
protected final boolean tryAcquire(int acquires) {
// 獲取當前執行緒
final Thread current = Thread.currentThread();
// 獲取同步狀態
int c = getState();
// 如果同步狀態為0,同時等待佇列中頭節點的後繼節點封裝的執行緒是當前執行緒,那麼才會通過CAS將同步狀態設定為1,表示獲取鎖(這個判斷就是用於保證公平鎖的)
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current); // 將擁有同步資源的執行緒設定為當前執行緒
return true;
}
}
// 如果同步狀態不為0,也就是鎖已經被執行緒所持有,同時如果當前執行緒就是擁有鎖的執行緒,它還嘗試獲取鎖,那麼就累加同步狀態的值,然後返回true
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
公平模式下的ReentrantLock的加鎖,將會呼叫公平同步器的lock()方法,該方法直接呼叫AQS的acquire()方法,該方法內部又呼叫公平同步器的tryAcquire()方法。
公平模式下的ReentrantLock的解鎖,將會呼叫AQS的release()方法,該方法又會呼叫抽象同步器提供的tryRelease()方法。
非公平模式下的總結
1.當執行緒要獲取鎖時,可以直接呼叫lock()和tryLock()方法。
2.如果呼叫lock()方法,那麼將會呼叫非公平同步器的lock()方法,該方法會先通過CAS嘗試獲取鎖,如果獲取鎖成功則直接返回,否則將會呼叫AQS的acquire()方法。
3.AQS的acquire()方法又會呼叫非公平同步器的tryAcquire()方法,該方法直接呼叫抽象同步器的nonfairTryAcquire()方法,如果同步狀態的值為0,則通過CAS嘗試獲取鎖,如果獲取鎖成功則返回true,否則返回false,同時如果同步狀態的值不為0,同時當前執行緒就是擁有鎖的執行緒,那麼允許它繼續進行加鎖,然後累加同步狀態的值,這種情況也會返回true。
4.如果呼叫tryLock()方法,那麼將會呼叫抽象同步器的nonfairTryAcquire()方法。
5.當執行緒要釋放鎖時,將會呼叫ReentrantLock的unlock()方法,該方法直接呼叫AQS的release()方法,該方法又會呼叫抽象同步器的tryRelease()方法,如果執行緒釋放鎖後,同步狀態的值為0,則返回true,否則說明執行緒加了不止一把鎖,那麼會更新同步狀態的值,然後返回false,只有當執行緒把它加的鎖都釋放後,tryRelease()方法才會返回true。
公平模式下的總結
1.當執行緒要獲取鎖時,可以直接呼叫lock()和tryLock()方法。
2.如果呼叫lock()方法,那麼將會呼叫公平同步器的lock()方法,該方法直接呼叫AQS的acquire()方法。
3.AQS的acquire()方法又會呼叫公平同步器的tryAcquire()方法,該方法中只有當同步狀態的值為0,同時等待佇列中的頭節點的後繼節點封裝的執行緒是當前執行緒時,才會通過CAS嘗試獲取鎖,如果獲取鎖成功則返回true,否則返回false,同時如果同步狀態的值不為0,同時當前執行緒就是擁有鎖的執行緒,那麼允許它繼續進行加鎖,然後累加同步狀態的值,這種情況也會返回true。
4.如果呼叫tryLock()方法,那麼將會呼叫抽象同步器的nonfairTryAcquire()方法。
5.當執行緒要釋放鎖時,將會呼叫ReentrantLock的unlock()方法,該方法直接呼叫AQS的release()方法,該方法又會呼叫抽象同步器的tryRelease()方法,如果執行緒釋放鎖後,同步狀態的值為0,則返回true,否則說明執行緒加了不止一把鎖,那麼會更新同步狀態的值,然後返回false,只有當執行緒把它加的鎖都釋放後,tryRelease()方法才會返回true。
FAQ
關於ReentrantLock的tryLock()方法
ReentrantLock的tryLock()方法是非公平的,因為無論在什麼模式下,ReentrantLock的tryLock()方法總是呼叫抽象同步器的nonfairTryAcquire()方法,因此當執行緒釋放鎖時,需要喚醒離頭節點最近的同時等待狀態不為CANCELLED的後繼節點,然後在該節點嘗試獲取鎖之前,其他執行緒直接呼叫了tryLock()方法獲取了鎖,那麼被喚醒的這個執行緒又只能再進入阻塞狀態,這就是非公平的體現。
關於獲取了鎖的執行緒能否再進行加鎖?
是可以的,因為無論在非公平模式下還是公平模式下,tryAcquire()方法當中都會有這麼一個判斷,也就是如果當前同步狀態的值不為0,表示鎖已經被執行緒所持有,同時當前執行緒就是擁有鎖的執行緒,那麼允許它繼續進行加鎖,然後累加同步狀態的值,這種情況方法也會返回true,同時在抽象同步器重寫的tryRelease()方法,如果執行緒釋放鎖後,同步狀態的值為0,則返回true,否則說明執行緒加了不止一把鎖,那麼會更新同步狀態的值,然後返回false,只有當執行緒把它加的鎖都釋放後,tryRelease()方法才會返回true。
非公平鎖是如何實現非公平的?
主要體現在非公平同步器的lock()方法,當執行緒要進行加鎖時,並沒有直接呼叫AQS的acquire()方法,而是先通過CAS嘗試獲取鎖,因此當執行緒釋放鎖時,需要喚醒離頭節點最近的同時等待狀態不為CANCELLED的後繼節點,然後在該節點嘗試獲取鎖之前,其他執行緒直接呼叫了lock()方法獲取了鎖,那麼被喚醒的這個執行緒又只能再進入阻塞狀態,這就是非公平的體現。
公平鎖是如何實現公平的?
主要體現在公平同步器的lock()和tryAcquire()方法,首先lock()方法直接呼叫AQS的acquire()方法,並沒有像非公平同步器的lock()方法一樣,先通過CAS嘗試獲取鎖,然後在tryAcquire()方法中,只有當同步狀態的值為0,同時等待佇列中的頭節點的後繼節點封裝的執行緒是當前執行緒時,才會通過CAS嘗試獲取鎖,因此當執行緒釋放鎖時,需要喚醒離頭節點最近的同時等待狀態不為CANCELLED的後繼節點,然後在該節點嘗試獲取鎖之前,其他執行緒呼叫了lock()方法進行加鎖,由於lock()方法直接呼叫AQS的acquire()方法,然後acquire()方法又呼叫公平同步器的tryAcquire()方法,雖然判斷到當前同步狀態的值為0,但是當前執行緒並不是等待佇列中頭節點的後繼節點封裝的執行緒,因此該執行緒也只能封裝成Node節點,然後加入到等待佇列當中。