上一篇文章對(duì)ReentrantLock鎖進(jìn)行了概述,相信看完了的話應(yīng)該對(duì)ReentrantLock鎖的使用有了一定的了解,這篇文章分析下ReentrantLock鎖的實(shí)現(xiàn)機(jī)制。
首先需要了解ReentrantLock類里面有三個(gè)靜態(tài)類:Sync
、NonfairSync
和FairSync
,ReentrantLock的鎖內(nèi)部實(shí)現(xiàn)通過(guò)NonfairSync和FairSync實(shí)現(xiàn),而NonfairSync和FairSync都是Sync的子類。類圖如下所示:
AbstractQueuedSynchronizer
,類如其名,抽象的隊(duì)列式的同步器,通常被稱之為AQS類,它是一個(gè)非常有用的父類,可用來(lái)定義鎖以及依賴于排隊(duì)park
線程(這里的park你可能不明白什么意思,但是你肯定知道阻塞的意思,其實(shí)park類似阻塞,很多文章直接就說(shuō)是阻塞,但是我感覺(jué)兩者還是不一樣的,park可以看做是一種輕量級(jí)的阻塞,至少我是這樣理解的,歡迎指正,unpark類似喚醒線程但是比喚醒線程高效)的其他同步器。AbstractQueuedLongSynchronizer 類提供相同的功能但擴(kuò)展了對(duì)同步狀態(tài)的 64 位的支持。兩者都擴(kuò)展了類 AbstractOwnableSynchronizer(一個(gè)幫助記錄當(dāng)前保持獨(dú)占同步的線程的簡(jiǎn)單類)。AQS框架是整個(gè)JUC鎖的核心部分,提供了以下內(nèi)容與功能:
- Node 節(jié)點(diǎn), Sync Queue和Condition Queue的存放的元素,用來(lái)保存park線程, 這些節(jié)點(diǎn)主要的區(qū)分在于 waitStatus 的值
- Condition Queue, 這個(gè)隊(duì)列是用于獨(dú)占模式中, 只有用到 Condition.awaitXX 時(shí)才會(huì)將 node加到 tail 上(PS: 在使用 Condition的前提是已經(jīng)獲取 Lock)
- Sync Queue, 獨(dú)占和共享的模式中均會(huì)使用到的存放 Node 的 CLH queue(主要特點(diǎn)是, 隊(duì)列中總有一個(gè) dummy 節(jié)點(diǎn), 后繼節(jié)點(diǎn)獲取鎖的條件由前繼節(jié)點(diǎn)決定, 前繼節(jié)點(diǎn)在釋放 lock 時(shí)會(huì)喚醒sleep中的后繼節(jié)點(diǎn)),CLH隊(duì)列的CLH全稱是Craig, Landin, and Hagersten
- ConditionObject, 用于獨(dú)占的模式, 主要是線程釋放lock, 加入 Condition Queue, 并進(jìn)行相應(yīng)的 signal 操作
- 獨(dú)占的獲取lock (acquire, release), 例如 ReentrantLock 就是使用這種,
- 共享的獲取lock (acquireShared, releaseShared), 例如 ReentrantReadWriteLock、Semaphore、CountDownLatch
所以,理解了AbstractQueuedSynchronizer 機(jī)制就理解了ReentrantLock鎖機(jī)制。
AbstractQueuedSynchronizer
繼承自AbstractOwnableSynchronizer
:
public abstract class AbstractOwnableSynchronizer implements java.io.Serializable {
/**所有的變量的都是transient的,為啥還要提供序列化ID呢?*/
private static final long serialVersionUID = 3737899427754241961L;
protected AbstractOwnableSynchronizer() { }
// The current owner of exclusive mode synchronization.
private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
exclusiveOwnerThread = thread;
}
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
}
AbstractOwnableSynchronizer
類名能夠很清晰的表達(dá)它的意思:獨(dú)有線程同步器,提供了線程獨(dú)占的兩個(gè)方法setExclusiveOwnerThread(Thread thread)
和getExclusiveOwnerThread()
內(nèi)部類 Node
AQS有一個(gè)很重要的內(nèi)部類Node,這個(gè)類會(huì)裝載線程,看下它的源碼:
static final class Node {
/** Marker to indicate a node is waiting in shared mode */
static final Node SHARED = new Node();
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
volatile int waitStatus;
volatile Node prev;
volatile Node next;
volatile Thread thread;
Node nextWaiter;
final boolean isShared() {//Returns true if node is waiting in shared mode.
return nextWaiter == SHARED;
}
final Node predecessor() throws NullPointerException {//
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
Node() { // Used to establish initial head or SHARED marker
}
Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}
可以看到,Node類里首先定義了6個(gè)static final的變量,用來(lái)區(qū)分是共享鎖還是獨(dú)占鎖以及等待線程的五種狀態(tài)(1、-1、-2、-3再額外加一個(gè)0的狀態(tài)),接著定義了四個(gè)volatile變量和一個(gè)Node類型的普通變量nextWaiter: Node。我們需要著重分析下這四個(gè)volatile變量和nextWaiter變量。
- volatile int waitStatus:
- CANCELLED值為1表示當(dāng)前節(jié)點(diǎn)裝載的線程因超時(shí)或者中斷而取消,Nodes never leave this state. In particular, a thread with cancelled node never again blocks.
- SIGNAL值為-1表示后繼節(jié)點(diǎn)被park了,如果當(dāng)前節(jié)點(diǎn)釋放鎖了或者裝載的線程取消了,那么需要unpark自己的后繼節(jié)點(diǎn),To avoid races, acquire methods must first indicate they need a signal, then retry the atomic acquire, and then, on failure, block.
- CONDITION值為-2表示在條件隊(duì)列(condition queue)中,It will not be used as a sync queue node until transferred, at which time the status will be set to 0. (Use of this value here has nothing to do with the other uses of the field, but simplifies mechanics.)
- PROPAGATE值為-3表示后續(xù)的acquireShared操作能夠得以執(zhí)行,A releaseShared should be propagated to other nodes. This is set (for head node only) in doReleaseShared to ensure propagation continues, even if other operations have since intervened.
- 值為0表示當(dāng)前節(jié)點(diǎn)在同步隊(duì)列(async queue)中等待著獲取鎖
- volatile Node prev:前驅(qū)節(jié)點(diǎn)
- volatile Node next:后繼節(jié)點(diǎn)
- volatile Thread thread:當(dāng)前節(jié)點(diǎn)裝載的線程
- Node nextWaiter:這個(gè)后繼節(jié)點(diǎn)和后繼節(jié)點(diǎn)next是有區(qū)別的,nextWaiter后繼節(jié)點(diǎn)是指在條件隊(duì)列(condition queue)中的后繼節(jié)點(diǎn)或者是共享節(jié)點(diǎn),因?yàn)闂l件鎖必須是獨(dú)占模式,在共享模式中我們也可以使用這個(gè)成員變量表示后繼節(jié)點(diǎn)節(jié)點(diǎn),這樣我們就節(jié)省了一個(gè)成員變量。注意只有nextWaiter而并沒(méi)有定義preWaiter。Link to next node waiting on condition, or the special value SHARED. Because condition queues are accessed only when holding in exclusive mode, we just need a simple linked queue to hold nodes while they are waiting on conditions. They are then transferred to the queue to re-acquire. And because conditions can only be exclusive, we save a field by using special value to indicate shared mode.
從上面我們可以看出,同步隊(duì)列async queue是一個(gè)雙向隊(duì)列,而條件隊(duì)列condition queue是一個(gè)單向隊(duì)列只有nextWaiter而沒(méi)有preWaiter。
關(guān)于waitStatus
這個(gè)變量,源碼中的注釋寫的非常清楚,我不翻譯了,大家細(xì)心看下:
The values are arranged numerically to simplify use. Non-negative values mean that a node doesn't need to signal. So, most code doesn't need to check for particular values, just for sign. The field is initialized to 0 for normal sync nodes, and CONDITION for condition nodes. It is modified using CAS (or when possible, unconditional volatile writes).
內(nèi)部類 ConditionObject
這個(gè)類是用來(lái)維護(hù)條件隊(duì)列(condition queue)的,這個(gè)類和LinkedList很像,就是一個(gè)單向的列表,里面維護(hù)了一個(gè)頭結(jié)點(diǎn)firstWaiter和尾節(jié)點(diǎn)lastWaiter以及兩個(gè)十分重要的方法await()
和signal()
。先來(lái)看下這個(gè)類的一個(gè)很核心的await
方法:
public final void await() throws InterruptedException {
//加入條件等待隊(duì)列前需要進(jìn)行中斷判斷
if (Thread.interrupted())
throw new InterruptedException();
//加入條件等待隊(duì)列
Node node = addConditionWaiter();
//釋放鎖
int savedState = fullyRelease(node);
//中斷標(biāo)記位
int interruptMode = 0;
/*死循環(huán)阻塞,直到被通知或者被中斷:
1) 當(dāng)被通知喚醒時(shí)還得判斷一下當(dāng)前節(jié)點(diǎn)是否已經(jīng)轉(zhuǎn)移到AQS同步隊(duì)列當(dāng)中(其實(shí)主動(dòng)通知的線程會(huì)確保其后繼等待節(jié)點(diǎn)轉(zhuǎn)移到同步隊(duì)列中,所以被通知后在下一次循環(huán)條件為false,繼續(xù)后續(xù)流程);
2) 當(dāng)被中斷喚醒時(shí)需要確保節(jié)點(diǎn)被轉(zhuǎn)移到同步隊(duì)列中,然后根據(jù)中斷發(fā)生在被通知前后位置設(shè)置中斷模式,并跳出循環(huán)
關(guān)于中斷模式:
1) 當(dāng)在被通知前被中斷則將中斷模式設(shè)置為THROW_IE;
2) 當(dāng)在被通知后則將中斷模式設(shè)置為REINTERRUPT(因?yàn)閍cquireQueued不會(huì)響應(yīng)中斷)。
*/
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
/*死循環(huán)獲取同步狀態(tài),并在同步狀態(tài)獲取成功或者取消獲取時(shí)設(shè)置中斷模式:
如果在被通知之后獲取鎖過(guò)程中發(fā)生中斷則將中斷模式設(shè)置為REINTERRUPT。
*/
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
//重新鏈接CONDITION節(jié)點(diǎn)。
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
//根據(jù)中斷模式拋出異常。
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
注意:被中斷的線程跳出while循環(huán)后,會(huì)調(diào)用acquireQueued方法自旋獲取鎖,嘗試獲取同步狀態(tài),而不是立即響應(yīng)中斷拋出中斷異常。在最后根據(jù)中斷模式來(lái)決定是否拋出異常。
再來(lái)看下signal()
方法:
//通知頭結(jié)點(diǎn)到同步隊(duì)列中去競(jìng)爭(zhēng)鎖,用于獨(dú)占模式
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
/*
從頭結(jié)點(diǎn)向后遍歷直到遇到一個(gè)非canceled或者null的節(jié)點(diǎn)
并將其移除條件等待隊(duì)列,并添加到同步隊(duì)列的尾部
*/
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
}
//當(dāng)頭結(jié)點(diǎn)轉(zhuǎn)移失敗后,繼續(xù)重試并確保更新后的頭結(jié)點(diǎn)不為null
while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
//如果CAS失敗,說(shuō)明節(jié)點(diǎn)在signal之前被cancel了,返回false
//CAS嘗試將節(jié)點(diǎn)的waitStatus從CONDITION-2修改為0
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
//進(jìn)入同步隊(duì)列,返回的p是這個(gè)節(jié)點(diǎn)在同步隊(duì)列中的前驅(qū)節(jié)點(diǎn)
Node p = enq(node);
//獲取前驅(qū)節(jié)點(diǎn)的狀態(tài)
int ws = p.waitStatus;
//1 如果前驅(qū)節(jié)點(diǎn)取消則直接喚醒當(dāng)前節(jié)點(diǎn)線程
//2 或者前驅(qū)節(jié)點(diǎn)沒(méi)取消的話,將前驅(qū)節(jié)點(diǎn)狀態(tài)設(shè)置為SIGNAL(-1),保證能被前驅(qū)節(jié)點(diǎn)通知到,如果設(shè)置失敗,直接喚醒當(dāng)前節(jié)點(diǎn)的線程
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
private static final boolean compareAndSetWaitStatus(Node node,
int expect,
int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset,
expect, update);
}
可以看到,更新waitStatus的操作是依賴Unsafe的CAS機(jī)制進(jìn)行的。
總結(jié):
- 每一個(gè)創(chuàng)建的ConditionObject都維持這各自的一個(gè)單向的等待隊(duì)列,但是每個(gè)ConditionObject都共享一個(gè)AQS的FIFO同步隊(duì)列,當(dāng)調(diào)用await方法時(shí)釋放鎖并進(jìn)入阻塞狀態(tài),調(diào)用signal方法將條件等待隊(duì)列中的首節(jié)點(diǎn)線程移動(dòng)到AQS同步隊(duì)列中并將其前繼節(jié)點(diǎn)設(shè)置為SIGNAL或者直接喚醒線程使得被通知的線程能去獲取鎖。
- 調(diào)用
await
方法釋放鎖并將線程添加到條件等待隊(duì)列中并沒(méi)有采用死循環(huán)CAS設(shè)置(參考AQS.enq方法),因?yàn)镃ondition對(duì)象只能用于獨(dú)占模式,而且在調(diào)用await之前會(huì)顯示的獲取獨(dú)占鎖,否則會(huì)拋出非法監(jiān)視器狀態(tài)異常。 - 調(diào)用
signal
方法將轉(zhuǎn)移等待節(jié)點(diǎn),也不需要CAS來(lái)保證,因?yàn)閟ignal會(huì)確保調(diào)用者caller是獲取獨(dú)占鎖的線程(通過(guò)isHeldExclusively方法來(lái)判斷,如果為false會(huì)拋出非法監(jiān)視器狀態(tài)的異常)。
AQS的同步狀態(tài)
AQS的int型成員變量state
表示同步狀態(tài),它是用volatile修飾:
private volatile int state;
在互斥鎖中它表示著線程是否已經(jīng)獲取了鎖,0未獲取,1已經(jīng)獲取了,大于1表示重入數(shù)。同時(shí)AQS提供了getState()、setState()、compareAndSetState()方法來(lái)獲取和修改該值:
可重入鎖指的是在一個(gè)線程中可以多次獲取同一把鎖,比如:一個(gè)線程在執(zhí)行一個(gè)帶鎖的方法,該方法中又調(diào)用了另一個(gè)需要相同鎖的方法,則該線程可以直接執(zhí)行調(diào)用的方法,而無(wú)需重新獲得鎖。synchronized鎖可以看做重入鎖。但要注意,獲取多少次鎖就要釋放多么次,這樣才能保證state是能回到零態(tài)的。
以ReentrantLock為例,state初始化為0,表示未鎖定狀態(tài)。A線程lock()時(shí),會(huì)調(diào)用tryAcquire()獨(dú)占該鎖并將state+1。此后,其他線程再tryAcquire()時(shí)就會(huì)失敗,直到A線程unlock()到state=0(即釋放鎖)為止,其它線程才有機(jī)會(huì)獲取該鎖。當(dāng)然,釋放鎖之前,A線程自己是可以重復(fù)獲取此鎖的(state會(huì)累加)。
所以可重入數(shù)大于1表示該線程可能調(diào)用了多個(gè)需要當(dāng)前鎖的方法,或同一個(gè)線程調(diào)用了多次lock()方法。
再以CountDownLatch以例,任務(wù)分為N個(gè)子線程去執(zhí)行,state也初始化為N(注意N要與線程個(gè)數(shù)一致)。這N個(gè)子線程是并行執(zhí)行的,每個(gè)子線程執(zhí)行完后countDown()一次,state會(huì)CAS減1。等到所有子線程都執(zhí)行完后(即state=0),會(huì)unpark()主調(diào)用線程,然后主調(diào)用線程就會(huì)從await()函數(shù)返回,繼續(xù)后余動(dòng)作。
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState;
}
protected final boolean compareAndSetState(int expect, int update) {
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
同步隊(duì)列async queue(CLH)
CLH是一個(gè)FIFO線程等待隊(duì)列,多線程爭(zhēng)用資源被阻塞時(shí)會(huì)進(jìn)入此隊(duì)列,它的頭節(jié)點(diǎn)時(shí)虛擬節(jié)點(diǎn)。
獨(dú)占鎖acquire/release方法解析
對(duì)于獲取鎖的流程,這張圖比直接貼代碼更加直觀,希望結(jié)合源碼仔細(xì)看下,圖片來(lái)自文獻(xiàn)3:- "當(dāng)前線程"首先通過(guò)
tryAcquire()
(大部分由子類實(shí)現(xiàn))嘗試獲取鎖。獲取成功的話,直接返回;嘗試失敗的話,進(jìn)入到等待隊(duì)列排序等待(前面還有可能有需要線程在等待該鎖)。 - "當(dāng)前線程"嘗試失敗的情況下,先通過(guò)addWaiter(Node.EXCLUSIVE)來(lái)將“當(dāng)前線程"加入到"CLH隊(duì)列"尾部。CLH隊(duì)列就是線程等待隊(duì)列。
- 執(zhí)行完addWaiter(Node.EXCLUSIVE)之后,調(diào)用acquireQueued方法獲取鎖,判斷當(dāng)前線程的Node == head,如果不是,調(diào)用park方法,讓當(dāng)前線程進(jìn)入休眠狀態(tài),等待喚醒;
- "當(dāng)前線程"在執(zhí)行acquireQueued()時(shí),會(huì)進(jìn)入到CLH隊(duì)列中休眠等待,直到獲取鎖了才返回!如果"當(dāng)前線程"在休眠等待過(guò)程中被中斷過(guò),acquireQueued會(huì)返回true,此時(shí)"當(dāng)前線程"會(huì)調(diào)用selfInterrupt()來(lái)自己給自己產(chǎn)生一個(gè)中斷。至于為什么要自己給自己產(chǎn)生一個(gè)中斷,后面再介紹。
- 當(dāng)當(dāng)前的Node是頭的時(shí)候,再次回調(diào)tryAcquire,子類業(yè)務(wù)邏輯修改整個(gè)AQS隊(duì)列的state的狀態(tài), 設(shè)置為頭節(jié)點(diǎn),next指針==null,方便GC,返回執(zhí)行對(duì)應(yīng)的業(yè)務(wù)邏輯;
對(duì)于鎖的釋放,源碼如下:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
它調(diào)用tryRelease()來(lái)釋放資源,這個(gè)tryRelease()方法一般都由子類實(shí)現(xiàn)。
在release()中“當(dāng)前線程”釋放鎖成功的話,會(huì)喚醒當(dāng)前線程的后繼線程。根據(jù)CLH隊(duì)列的FIFO規(guī)則,“當(dāng)前線程”(即已經(jīng)獲取鎖的線程)肯定是head;如果CLH隊(duì)列非空的話,則喚醒鎖的下一個(gè)等待線程。看下unparkSuccessor()的源碼,它在AQS中實(shí)現(xiàn):
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
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);
}
用unpark()喚醒等待隊(duì)列中最前邊的那個(gè)未放棄線程,這里我們也用s來(lái)表示吧。此時(shí),再和acquireQueued()聯(lián)系起來(lái),s被喚醒后,進(jìn)入if (p == head && tryAcquire(arg))的判斷(即使p!=head也沒(méi)關(guān)系,它會(huì)再進(jìn)入shouldParkAfterFailedAcquire()尋找一個(gè)離頭節(jié)點(diǎn)最近的一個(gè)等待位置。這里既然s已經(jīng)是等待隊(duì)列中最前邊的那個(gè)未放棄線程了,那么通過(guò)shouldParkAfterFailedAcquire()的調(diào)整,s也必然會(huì)跑到head的next結(jié)點(diǎn),下一次循環(huán)判斷p==head就成立了),然后s把自己設(shè)置成head標(biāo)桿結(jié)點(diǎn),表示自己已經(jīng)獲取到資源了,acquire()也返回了。
共享鎖acquireShared/releaseShared方法解析
acquireShared(int)
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
tryAcquireShared()方法依然需要自定義同步器去實(shí)現(xiàn),但是AQS已經(jīng)把其返回值的語(yǔ)義定義好了:負(fù)值代表獲取失敗;0代表獲取成功,但沒(méi)有剩余資源;正數(shù)表示獲取成功,還有剩余資源,其他線程還可以去獲取。所以這里acquireShared()的流程就是:
- tryAcquireShared()嘗試獲取資源,成功則直接返回;
- 失敗則通過(guò)doAcquireShared()進(jìn)入等待隊(duì)列,直到獲取到資源為止才返回。
看下doAcquireShared源碼:
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);//加入隊(duì)列尾部
boolean failed = true;//是否成功標(biāo)志
try {
boolean interrupted = false;//等待過(guò)程中是否被中斷過(guò)的標(biāo)志
for (;;) {
final Node p = node.predecessor();//前驅(qū)
if (p == head) {//如果到head的下一個(gè),因?yàn)閔ead是拿到資源的線程,此時(shí)node被喚醒,很可能是head用完資源來(lái)喚醒自己的
int r = tryAcquireShared(arg);//嘗試獲取資源
if (r >= 0) {//成功
setHeadAndPropagate(node, r);//將head指向自己,還有剩余資源可以再喚醒之后的線程
p.next = null; // help GC
if (interrupted)//如果等待過(guò)程中被打斷過(guò),此時(shí)將中斷補(bǔ)上。
selfInterrupt();
failed = false;
return;
}
}
//判斷狀態(tài),尋找安全點(diǎn),進(jìn)入waiting狀態(tài),等著被unpark()或interrupt()
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
重點(diǎn)分析一下獲取鎖后的操作:setHeadAndPropagate:
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // Record old head for check below
setHead(node);
/*
* Try to signal next queued node if:
* Propagation was indicated by caller,
* or was recorded (as h.waitStatus either before
* or after setHead) by a previous operation
* (note: this uses sign-check of waitStatus because
* PROPAGATE status may transition to SIGNAL.)
* and
* The next node is waiting in shared mode,
* or we don't know, because it appears null
*
* The conservatism in both of these checks may cause
* unnecessary wake-ups, but only when there are multiple
* racing acquires/releases, so most need signals now or soon
* anyway.
*/
/**
* 如果讀鎖(共享鎖)獲取成功,或頭部節(jié)點(diǎn)為空,或頭節(jié)點(diǎn)取消,或剛獲取讀鎖的線程的下一個(gè)節(jié)點(diǎn)為空,
* 或在節(jié)點(diǎn)的下個(gè)節(jié)點(diǎn)也在申請(qǐng)讀鎖,則在CLH隊(duì)列中傳播下去喚醒線程,怎么理解這個(gè)傳播呢,
* 就是只要獲取成功到讀鎖,那就要傳播到下一個(gè)節(jié)點(diǎn)(如果一下個(gè)節(jié)點(diǎn)繼續(xù)是讀鎖的申請(qǐng),
* 只要成功獲取,就再下一個(gè)節(jié)點(diǎn),直到隊(duì)列尾部或?yàn)閷戞i的申請(qǐng),停止傳播)。具體請(qǐng)看doReleaseShared方法。
*
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
其實(shí)跟acquire()的流程大同小異,只不過(guò)多了個(gè)自己拿到資源后,還會(huì)去喚醒后繼隊(duì)友的操作,這體現(xiàn)了共享鎖的定義。
releaseShared()
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
此方法的流程也比較簡(jiǎn)單,一句話:釋放掉資源后,喚醒后繼。跟獨(dú)占模式下的release()相似,但有一點(diǎn)稍微需要注意:獨(dú)占模式下的tryRelease()在完全釋放掉資源(state=0)后,才會(huì)返回true去喚醒其他線程,這主要是基于可重入的考量;而共享模式下的releaseShared()則沒(méi)有這種要求,一是共享的實(shí)質(zhì)--多線程可并發(fā)執(zhí)行;二是共享模式基本也不會(huì)重入吧(至少我還沒(méi)見(jiàn)過(guò)),所以自定義同步器可以根據(jù)需要決定返回值。
doReleaseShared()源碼:
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) { // 從隊(duì)列的頭部開(kāi)始遍歷每一個(gè)節(jié)點(diǎn)
int ws = h.waitStatus;
// 如果節(jié)點(diǎn)狀態(tài)為 Node.SIGNAL,將狀態(tài)設(shè)置為0,設(shè)置成功,喚醒線程。
// 為什么會(huì)設(shè)置不成功,可能改節(jié)點(diǎn)被取消;還有一種情況就是有多個(gè)線程在運(yùn)行該代碼段,這就是PROPAGATE的含義吧。
// 如果一個(gè)節(jié)點(diǎn)的狀態(tài)設(shè)置為 Node.SIGNAL,則說(shuō)明它有后繼節(jié)點(diǎn),并且處于阻塞狀態(tài)
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
// 如果狀態(tài)為0,則設(shè)置為Node.PROPAGATE,設(shè)置為傳播,該值然后會(huì)在什么時(shí)候變化呢?
// 在判斷該節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)是否需要阻塞時(shí),會(huì)判斷,如果狀態(tài)不是Node.SIGNAL或取消狀態(tài),
// 為了保險(xiǎn)起見(jiàn),會(huì)將前置節(jié)點(diǎn)狀態(tài)設(shè)置為Node.SIGNAL,然后再次判斷,是否需要阻塞。
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
/**
* 如果處理過(guò)一次 unparkSuccessor 方法后,頭節(jié)點(diǎn)沒(méi)有發(fā)生變化,就退出該方法,那head在什么時(shí)候會(huì)改變呢?
* 當(dāng)然在是搶占鎖成功的時(shí)候,head節(jié)點(diǎn)代表獲取鎖的節(jié)點(diǎn)。一旦獲取鎖成功,則又會(huì)進(jìn)入setHeadAndPropagate方法,
* 當(dāng)然又會(huì)觸發(fā)doReleaseShared方法,傳播特性應(yīng)該就是表現(xiàn)在這里吧。再想一下,同一時(shí)間,可以有多個(gè)多線程占有鎖,
* 那在鎖釋放時(shí),寫鎖的釋放比較簡(jiǎn)單,就是從頭部節(jié)點(diǎn)下的第一個(gè)非取消節(jié)點(diǎn),喚醒線程即可,
* 為了在釋放讀鎖的上下文環(huán)境中獲取代表讀鎖的線程,將信息存入在 readHolds ThreadLocal變量中。
*/
if (h == head) // loop if head changed
break;
}
}
參考文獻(xiàn)