開(kāi)篇
用圖形化的方式加深加鎖和解鎖過(guò)程的解釋性。
java源碼 - ReentrantLock
java源碼 - ReentrantLock之FairSync
java源碼 - ReentrantLock之NonfairSync
java源碼 - ReentrantLock圖解加鎖過(guò)程
加鎖流程
- 1、首先我們分析非公平鎖的的請(qǐng)求過(guò)程。我們假設(shè)在這個(gè)時(shí)候,還沒(méi)有任務(wù)線程獲取鎖,這個(gè)時(shí)候,第一個(gè)線程過(guò)來(lái)了(我們使用的是非公平鎖),那么第一個(gè)線程thread1會(huì)去獲取鎖.
這時(shí)它會(huì)調(diào)用下面的方法,通過(guò)CAS的操作,將當(dāng)前AQS的state由0變成1,證明當(dāng)前thread1已經(jīng)獲取到鎖,并且將AQS的exclusiveOwnerThread設(shè)置成thread1,證明當(dāng)前持有鎖的線程是thread1。:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
- 2、此時(shí)來(lái)了第二個(gè)線程thread2,并且我們假設(shè)thread1還沒(méi)有釋放鎖,因?yàn)槲覀兪褂玫氖欠枪芥i,那么thread2首先會(huì)進(jìn)行搶占式的去獲取鎖,調(diào)用NonFairSync.lock方法獲取鎖。
NonFairSync.lock方法的第一個(gè)分支是通過(guò)CAS操作獲取鎖,很明顯,這一步肯定會(huì)失敗,因?yàn)榇藭r(shí)thread1還沒(méi)有釋放鎖。那么thread2將會(huì)走NonFairSync.lock方法的第二個(gè)分支,進(jìn)行acquire(1)操作。
acquire(1)其實(shí)是AQS的方法,acquire(1)方法內(nèi)部首先調(diào)用tryAcquire方法,ReentrantLock.NonFairLock重寫了tryAcquire方法,并且ReentrantLock.NonFairLock的tryAcquire方法又調(diào)用了ReentrantLock.Sync的nonfairTryAcquire方法.
nonfairTryAcquire方法如下:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
這個(gè)方法的執(zhí)行邏輯如下:
1. 獲取當(dāng)前將要去獲取鎖的線程,在此時(shí)的情況下,也就是我們的thread2線程。
2. 獲取當(dāng)前AQS的state的值。如果此時(shí)state的值是0,那么我們就通過(guò)CAS操作獲取鎖,然后設(shè)置AQS的exclusiveOwnerThread為thread2。很明顯,在當(dāng)前的這個(gè)執(zhí)行情況下,state的值是1不是0,因?yàn)槲覀兊膖hread1還沒(méi)有釋放鎖。
3. 如果當(dāng)前將要去獲取鎖的線程等于此時(shí)AQS的exclusiveOwnerThread的線程,則此時(shí)將state的值加1,很明顯這是重入鎖的實(shí)現(xiàn)方式。在此時(shí)的運(yùn)行狀態(tài)下,將要去獲取鎖的線程不是thread1,也就是說(shuō)這一步不成立。
4. 以上操作都不成立的話,我們直接返回false。
既然返回了false,那么之后就會(huì)調(diào)用addWaiter方法,這個(gè)方法負(fù)責(zé)把當(dāng)前無(wú)法獲取鎖的線程包裝為一個(gè)Node添加到隊(duì)尾。通過(guò)下面的代碼片段我們就知道調(diào)用邏輯:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
我們進(jìn)入到addWaiter方法內(nèi)部去看:
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
很明顯在addWaiter內(nèi)部:
第一步:將當(dāng)前將要去獲取鎖的線程也就是thread2和獨(dú)占模式封裝為一個(gè)node對(duì)象。并且我們也知道在當(dāng)前的執(zhí)行環(huán)境下,線程阻塞隊(duì)列是空的,因?yàn)閠hread1獲取了鎖,thread2也是剛剛來(lái)請(qǐng)求鎖,所以線程阻塞隊(duì)列里面是空的。很明顯,這個(gè)時(shí)候隊(duì)列的尾部tail節(jié)點(diǎn)也是null,那么將直接進(jìn)入到enq方法。
第二步:我們首先看下enq方法的內(nèi)部實(shí)現(xiàn)。首先內(nèi)部是一個(gè)自懸循環(huán)。
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
第一次循環(huán):
t為null,隨后我們new出了一個(gè)空的node節(jié)點(diǎn),并且通過(guò)CAS操作設(shè)置了線程的阻塞隊(duì)列的head節(jié)點(diǎn)就是我們剛才new出來(lái)的那個(gè)空的node節(jié)點(diǎn),其實(shí)這是一個(gè)“假節(jié)點(diǎn)”,那么什么是“假節(jié)點(diǎn)”呢?那就是節(jié)點(diǎn)中不包含線程。設(shè)置完head節(jié)點(diǎn)后,同時(shí)又將head節(jié)點(diǎn)賦值給尾部tail節(jié)點(diǎn),到此第一次循環(huán)結(jié)束。此時(shí)的節(jié)點(diǎn)就是如下:

第二次循環(huán):
現(xiàn)在判斷尾部tail已經(jīng)不是null了,那么就走第二個(gè)分支了。將尾部tail節(jié)點(diǎn)賦值給我們傳遞進(jìn)來(lái)的節(jié)點(diǎn)Node的前驅(qū)節(jié)點(diǎn),此時(shí)的結(jié)構(gòu)如下:

然后再通過(guò)CAS的操作,將我們傳遞進(jìn)來(lái)的節(jié)點(diǎn)node設(shè)置成尾部tail節(jié)點(diǎn),并且將我們的node節(jié)點(diǎn)賦值給原來(lái)的老的那個(gè)尾部節(jié)點(diǎn)的后繼節(jié)點(diǎn),此時(shí)的結(jié)構(gòu)如下:

這個(gè)時(shí)候代碼中使用了return關(guān)鍵字,也就是證明我們經(jīng)過(guò)了2次循環(huán)跳出了這個(gè)自懸循環(huán)體系。
按照代碼的執(zhí)行流程,接下來(lái)將會(huì)調(diào)用acquireQueued方法,主要是判斷當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是不是head節(jié)點(diǎn),如果是的話,就再去嘗試獲取鎖,如果不是,就掛起當(dāng)前線程。這里可能有人疑問(wèn)了,為什么判斷當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是head節(jié)點(diǎn)的話就去嘗試獲取鎖呢?因?yàn)槲覀冎纇ead節(jié)點(diǎn)是一個(gè)假節(jié)點(diǎn),如果當(dāng)前的節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是頭節(jié)點(diǎn)即是假節(jié)點(diǎn)的話,那么這個(gè)假節(jié)點(diǎn)的后繼節(jié)點(diǎn)就有可能有獲取鎖的機(jī)會(huì),所以我們需要去嘗試。
現(xiàn)在我們看下acquireQueued方法內(nèi)部,我們也可以清楚的看到,這個(gè)方法的內(nèi)部也是一個(gè)自懸循環(huán)。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
第一次循環(huán):獲取我們傳入node的前驅(qū)節(jié)點(diǎn),判斷是否是head節(jié)點(diǎn),現(xiàn)在我們的狀態(tài)是:

很明顯滿足當(dāng)前node節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是head節(jié)點(diǎn),那么現(xiàn)在我們就要去調(diào)用tryAcquire方法,也就是NonfairSync類的tryAcquire方法,而這個(gè)方法又調(diào)用了ReentrantLock.Sync.nonfairTryAcquire方法。
很明顯此時(shí)thread2獲取鎖是失敗的,直接返回false。按照調(diào)用流程,現(xiàn)在進(jìn)入了當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的shouldParkAfterFailedAcquire方法,檢查當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的waitstatus。shouldParkAfterFailedAcquire方法內(nèi)部如下:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
- 1. 如果前驅(qū)節(jié)點(diǎn)的waitStatus為-1,也就是SIGNAL,就返回true。
- 2. 如果當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的waitstatus大于0,也就是說(shuō)被CANCEL掉了,這個(gè)時(shí)候我們會(huì)除掉這個(gè)節(jié)點(diǎn)。
- 3. 如果都不是以上的情況,就通過(guò)CAS操作將這個(gè)前驅(qū)節(jié)點(diǎn)設(shè)置成SIGHNAL。
很明顯,我們?cè)谶@里的情況是第3種情況,并且這個(gè)方法運(yùn)行后返回false。此時(shí)的結(jié)構(gòu)如下,主要是head節(jié)點(diǎn)的waitStatus由0變成了-1。

第二次循環(huán):獲取我們傳入node的前驅(qū)節(jié)點(diǎn),判斷是否是head節(jié)點(diǎn),現(xiàn)在我們的狀態(tài)是:

很明顯滿足當(dāng)前node節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是head節(jié)點(diǎn),那么現(xiàn)在我們就要去調(diào)用tryAcquire方法,也就是NonfairSync類的tryAcquire方法,而這個(gè)方法又調(diào)用了ReentrantLock.Sync.nonfairTryAcquire方法。
很明顯此時(shí)thread2獲取鎖是失敗的,直接返回false。按照調(diào)用流程,現(xiàn)在進(jìn)入了當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的shouldParkAfterFailedAcquire方法,檢查當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的waitstatus。此時(shí)waitstatus為-1,這個(gè)方法返回true。
shouldParkAfterFailedAcquire返回true后,就會(huì)調(diào)用parkAndCheckInterrupt方法,直接將當(dāng)前線程thread2阻塞。
仔細(xì)看這個(gè)方法acquireQueued方法,是無(wú)限循環(huán),感覺(jué)如果p == head && tryAcquire(arg)條件不滿足循環(huán)將永遠(yuǎn)無(wú)法結(jié)束,在這里,當(dāng)然不會(huì)出現(xiàn)死循環(huán)。因?yàn)閜arkAndCheckInterrupt會(huì)把當(dāng)前線程阻塞。分析到這里,我們的thread2線程已經(jīng)被阻塞了,這個(gè)線程不會(huì)再繼續(xù)執(zhí)行下去了。
- 3、假設(shè)現(xiàn)在我們的thread1還沒(méi)有釋放鎖,而現(xiàn)在又來(lái)了一個(gè)線程thread3。
thread3首先調(diào)用lock方法獲取鎖,首先去搶占鎖,因?yàn)槲覀冎纓hread1還沒(méi)有釋放鎖,這個(gè)時(shí)候thread3肯定搶占失敗,于是又調(diào)用了acquire方法,接著又失敗。接著會(huì)去調(diào)用addWaiter方法,將當(dāng)前線程thread3封裝成node加入到線程阻塞隊(duì)列的尾部?,F(xiàn)在的結(jié)構(gòu)如下:

addWaiter如下:
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
第一步:將當(dāng)前將要去獲取鎖的線程也就是thread3和獨(dú)占模式封裝為一個(gè)node對(duì)象。并且我們也知道在當(dāng)前的執(zhí)行環(huán)境下,線程阻塞隊(duì)列不是空的,因?yàn)閠hread2獲取了鎖,thread2已經(jīng)加入了隊(duì)列。
很明顯,這個(gè)時(shí)候隊(duì)列的尾部tail節(jié)點(diǎn)也不是null,那么將直接進(jìn)入到if分支。將尾部tail節(jié)點(diǎn)賦值給我們傳入的node節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)。如下:

第二步:通過(guò)CAS將我們傳遞進(jìn)來(lái)的node節(jié)點(diǎn)設(shè)置成tail節(jié)點(diǎn),并且將新tail節(jié)點(diǎn)設(shè)置成老tail節(jié)點(diǎn)的后繼節(jié)點(diǎn)。

到此,addWaiter方法執(zhí)行完畢,接著執(zhí)行acquireQueued方法。這是一個(gè)自循環(huán)方法。
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
第一次循環(huán):獲取我們傳入node的前驅(qū)節(jié)點(diǎn),判斷是否是head節(jié)點(diǎn),現(xiàn)在我們的狀態(tài)是:

我們傳入node的前驅(qū)節(jié)點(diǎn)不是head節(jié)點(diǎn),那么直接走第二個(gè)if分支,調(diào)用shouldParkAfterFailedAcquire方法。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
- 1. 如果前驅(qū)節(jié)點(diǎn)的waitStatus為-1,也就是SIGNAL,就返回true。
- 2. 如果當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的waitstatus大于0,也就是說(shuō)被CANCEL掉了,這個(gè)時(shí)候我們會(huì)除掉這個(gè)節(jié)點(diǎn)。
- 3. 如果都不是以上的情況,就通過(guò)CAS操作將這個(gè)前驅(qū)節(jié)點(diǎn)設(shè)置成SIGHNAL。
很明顯,我們?cè)谶@里的情況是第3種情況,并且這個(gè)方法運(yùn)行后返回false。
此時(shí)的結(jié)構(gòu)如下,主要是t節(jié)點(diǎn)的waitStatus由0變成了-1。

第二次循環(huán):獲取我們傳入node的前驅(qū)節(jié)點(diǎn),判斷是否是head節(jié)點(diǎn),現(xiàn)在我們的狀態(tài)是:

很明顯我們傳入node的前驅(qū)節(jié)點(diǎn)不是head節(jié)點(diǎn),那么直接進(jìn)入shouldParkAfterFailedAcquire方法。
1. 如果前驅(qū)節(jié)點(diǎn)的waitStatus為-1,也就是SIGNAL,就返回true。
2. 如果當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的waitstatus大于0,也就是說(shuō)被CANCEL掉了,這個(gè)時(shí)候我們會(huì)除掉這個(gè)節(jié)點(diǎn)。
3. 如果都不是以上的情況,就通過(guò)CAS操作將這個(gè)前驅(qū)節(jié)點(diǎn)設(shè)置成SIGHNAL。
很明顯,我們?cè)谶@里的情況是第1種情況,并且這個(gè)方法運(yùn)行后返回true。
然后就會(huì)調(diào)用parkAndCheckInterrupt方法,直接將當(dāng)前線程thread3阻塞?,F(xiàn)在thread2和thread3都已經(jīng)被阻塞。
釋放鎖的過(guò)程
現(xiàn)在thread1要開(kāi)始釋放鎖了。調(diào)用unlock方法,unlock方法又調(diào)用了內(nèi)部的release方法:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
- 獲取當(dāng)前AQS的state,并減去1,判斷當(dāng)前線程是否等于AQS的exclusiveOwnerThread,如果不是,就拋異常,這就保證了加鎖和釋放鎖必須是同一個(gè)線程。
- 如果(state-1)的結(jié)果不為0,說(shuō)明鎖被重入了,需要多次unlock。
如果(state-1)等于0,我們就將AQS的ExclusiveOwnerThread設(shè)置為null。 -
如果上述操作成功了,也就是tryRelase方法返回了true,那么就會(huì)判斷當(dāng)前隊(duì)列中的head節(jié)點(diǎn),當(dāng)前結(jié)構(gòu)如下:
如果head節(jié)點(diǎn)不為null,并且head節(jié)點(diǎn)的waitStatus不為0, 我們就調(diào)用unparkSuccessor方法去喚醒head節(jié)點(diǎn)的后繼節(jié)點(diǎn)。
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
第一步:獲取head節(jié)點(diǎn)的waitStatus,如果小于0,就通過(guò)CAS操作將head節(jié)點(diǎn)的waitStatus修改為0,現(xiàn)在是:

第二步:尋找head節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn),如果這個(gè)節(jié)點(diǎn)的waitStatus小于0,就喚醒這個(gè)節(jié)點(diǎn),否則遍歷下去,找到第一個(gè)waitStatus<=0的節(jié)點(diǎn),并喚醒。現(xiàn)在thread2線程被喚醒了,我們知道剛才thread2在acquireQueued被中斷,現(xiàn)在繼續(xù)執(zhí)行,又進(jìn)入了for循環(huán),當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是head并且調(diào)用tryAquire方法獲得鎖并且成功。那么設(shè)置當(dāng)前Node為head節(jié)點(diǎn),將里面的thead和prev設(shè)置為null。

調(diào)用完畢后,acquireQueued返回false。并且現(xiàn)在thread2自由了。到此,已經(jīng)全部分析完畢。
