
ReentrantLock 介紹
一個(gè)可重入的互斥鎖,它具有與使用{synchronized}方法和語(yǔ)句訪問(wèn)的隱式監(jiān)視器鎖相同的基本行為和語(yǔ)義,但它具有可擴(kuò)展的能力。
一個(gè)ReentrantLock會(huì)被最后一次成功鎖定(lock)的線程擁有,在還沒(méi)解鎖(unlock)之前。當(dāng)鎖沒(méi)有被其他線程擁有的話,一個(gè)線程執(zhí)行『lock』方法將會(huì)返回,獲取鎖成功。一個(gè)方法將會(huì)立即的返回,如果當(dāng)前線程已經(jīng)擁有了這個(gè)鎖。可以使用『isHeldByCurrentThread』和『getHoldCount』來(lái)檢查當(dāng)前線程是否持有鎖。
這個(gè)類的構(gòu)造方法會(huì)接受一個(gè)可選的“fairness”參數(shù)。當(dāng)該參數(shù)設(shè)置為true時(shí),在發(fā)生多線程競(jìng)爭(zhēng)時(shí),鎖更傾向?qū)⑹褂脵?quán)授予給最長(zhǎng)等待時(shí)間的線程。另外,鎖不保證任何特定的訪問(wèn)順序。程序在多線程情況下使用公平鎖來(lái)訪問(wèn)的話可能表現(xiàn)出較低的吞吐量(如,較慢;經(jīng)常慢很多)與比使用默認(rèn)設(shè)置相比,但是在獲取鎖上有較小的時(shí)間差異,并保證不會(huì)有饑餓(線程)。然而需要注意的是,公平鎖并不保證線程調(diào)度的公平性。(也就是說(shuō),即使使用公平鎖,也無(wú)法確保線程調(diào)度器是公平的。如果線程調(diào)度器選擇忽略一個(gè)線程,而該線程為了這個(gè)鎖已經(jīng)等待了很長(zhǎng)時(shí)間,那么就沒(méi)有機(jī)會(huì)公平地處理這個(gè)鎖了)
還需要注意的是,沒(méi)有時(shí)間參數(shù)的『tryLock()』方法是沒(méi)有信譽(yù)的公平設(shè)置。它將會(huì)成功如果鎖是可獲取的,即便有其他線程正在等待獲取鎖。
除了對(duì)Lock接口的實(shí)現(xiàn)外,這個(gè)類還定義了一系列的public和protected方法用于檢測(cè)lock的state。這些方法中的某些方法僅用于檢測(cè)和監(jiān)控。
這個(gè)類的序列化行為同lock內(nèi)置的行為是一樣的:一個(gè)反序列化的鎖的狀態(tài)(state)是未鎖定的(unlocked),無(wú)論它序列化時(shí)的狀態(tài)(state)是什么。
這個(gè)鎖支持同一個(gè)線程最大遞歸獲取鎖2147483647(即,Integer.MAX_VALUE)次。如果嘗試獲取鎖的次數(shù)操作了這個(gè)限制,那么一個(gè)Error獲取lock方法中拋出。
AbstractQueuedSynchronizer
ReentrantLock的公平鎖和非公平鎖都是基于AbstractQueuedSynchronizer(AQS)實(shí)現(xiàn)的。ReentrantLock使用的是AQS的排他鎖模式,由于AQS除了排他鎖模式還有共享鎖模式,本文僅對(duì)ReentrantLock涉及到的排他鎖模式部分的內(nèi)容進(jìn)行介紹,關(guān)于共享鎖模式的部分會(huì)在 CountDownLatch 源碼淺析一文中介紹。
AQS提供一個(gè)框架用于實(shí)現(xiàn)依賴于先進(jìn)先出(FIFO)等待隊(duì)列的阻塞鎖和同步器(信號(hào)量,事件等)。這個(gè)類被設(shè)計(jì)與作為一個(gè)有用的基類,一個(gè)依賴單一原子值為代表狀態(tài)的多種同步器的基類。子類必須將修改這個(gè)狀態(tài)值的方法定義為受保護(hù)的方法,并且該方法會(huì)根據(jù)對(duì)象(即,AbstractQueuedSynchronizer子類)被獲取和釋放的方式來(lái)定義這個(gè)狀態(tài)。根據(jù)這些,這個(gè)類的其他方法實(shí)現(xiàn)所有排隊(duì)和阻塞的機(jī)制。子類能夠維護(hù)其他的狀態(tài)屬性,但是只有使用『getState』方法、『setState』方法以及『compareAndSetState』方法來(lái)原子性的修改 int 狀態(tài)值的操作才能遵循相關(guān)同步性。
等待隊(duì)列節(jié)點(diǎn)類 ——— Node
等待隊(duì)列是一個(gè)CLH鎖隊(duì)列的變體。CLH通常被用于自旋鎖(CLH鎖是一種基于鏈表的可擴(kuò)展、高性能、公平的自旋鎖,申請(qǐng)線程只在本地變量上自旋,它不斷輪詢前驅(qū)的狀態(tài),如果發(fā)現(xiàn)前驅(qū)釋放了鎖就結(jié)束自旋。)。我們用它來(lái)代替阻塞同步器,但是使用相同的基本策略,該策略是持有一些關(guān)于一個(gè)線程在它前驅(qū)節(jié)點(diǎn)的控制信息。一個(gè)“status”字段在每個(gè)節(jié)點(diǎn)中用于保持追蹤是否一個(gè)線程需要被阻塞。一個(gè)節(jié)點(diǎn)會(huì)得到通知當(dāng)它的前驅(qū)節(jié)點(diǎn)被釋放時(shí)。隊(duì)列中的每一個(gè)節(jié)點(diǎn)都作為一個(gè)持有單一等待線程的特定通知風(fēng)格的監(jiān)視器。狀態(tài)字段不會(huì)控制線程是否被授予鎖等。一個(gè)線程可能嘗試去獲取鎖如果它在隊(duì)列的第一個(gè)。但是首先這并不保證成功,它只是給與了競(jìng)爭(zhēng)的權(quán)力(也就是說(shuō),隊(duì)列中第一個(gè)線程嘗試獲取鎖時(shí),并不保證一定能得到鎖,它只是有競(jìng)爭(zhēng)鎖的權(quán)力而已)。所以當(dāng)前被釋放的競(jìng)爭(zhēng)者線程可能需要重新等待獲取鎖。
(這里說(shuō)的"隊(duì)列中的第一個(gè)的線程"指的時(shí),從隊(duì)列頭開(kāi)始往下的節(jié)點(diǎn)中,第一個(gè)node.thread != null的線程。因?yàn)?,AQS隊(duì)列的head節(jié)點(diǎn)是一個(gè)虛節(jié)點(diǎn),不是有個(gè)有效的等待節(jié)點(diǎn),因此head節(jié)點(diǎn)的thread是為null的。)
為了排隊(duì)進(jìn)入一個(gè)CLH鎖,你可以原子性的拼接節(jié)點(diǎn)到隊(duì)列中作為一個(gè)新的隊(duì)尾;對(duì)于出隊(duì),你只要設(shè)置頭字段。(即,入隊(duì)操作時(shí)新的節(jié)點(diǎn)會(huì)排在CLH鎖隊(duì)列的隊(duì)尾,而出隊(duì)操作就是將待出隊(duì)的node設(shè)置為head。由此可見(jiàn),在AQS中維護(hù)的這個(gè)等待隊(duì)列,head是一個(gè)無(wú)效的節(jié)點(diǎn)。初始化時(shí)head是一個(gè)new Node()節(jié)點(diǎn);在后期的操作中,需要出隊(duì)的節(jié)點(diǎn)就會(huì)設(shè)置到head中。)
+------+ prev +-----+ +-----+
head | | <---- | | <---- | | tail
+------+ +-----+ +-----+
插入到一個(gè)CLH隊(duì)列的請(qǐng)求只是一個(gè)對(duì)“tail”的單個(gè)原子操作,所以有一個(gè)簡(jiǎn)單的從未入隊(duì)到入隊(duì)的原子分割點(diǎn)。類似的,出隊(duì)調(diào)用只需要修改“head”。然而,節(jié)點(diǎn)需要更多的工作來(lái)確定他們的后繼者是誰(shuí),部分是為了處理由于超時(shí)和中斷而導(dǎo)致的可能的取消。
(也就是說(shuō),一個(gè)node的后繼節(jié)點(diǎn)不一定就是node.next,因?yàn)殛?duì)列中的節(jié)點(diǎn)可能因?yàn)槌瑫r(shí)或中斷而取消了,而這些取消的節(jié)點(diǎn)此時(shí)還沒(méi)被移除隊(duì)列(也許正在移除隊(duì)列的過(guò)程中),而一個(gè)node的后繼節(jié)點(diǎn)指的是一個(gè)未被取消的有效節(jié)點(diǎn),因此在下面的操作中你就會(huì)發(fā)現(xiàn),在尋找后繼節(jié)點(diǎn)時(shí),尋找的都是當(dāng)前節(jié)點(diǎn)后面第一個(gè)有效節(jié)點(diǎn),即非取消節(jié)點(diǎn)。)
“prev”(前驅(qū))連接(原始的CLH鎖是不使用前驅(qū)連接的),主要用于處理取消。如果一個(gè)節(jié)點(diǎn)被取消了,它的后驅(qū)(通常)會(huì)重連接到一個(gè)未被取消的前驅(qū)。
另外我們使用“next”連接去實(shí)現(xiàn)阻塞機(jī)制。每個(gè)節(jié)點(diǎn)的線程ID被它們自己的節(jié)點(diǎn)所持有,所以前驅(qū)節(jié)點(diǎn)通知下一個(gè)節(jié)點(diǎn)可以被喚醒,這是通過(guò)遍歷下一個(gè)鏈接(即,next字段)來(lái)確定需要喚醒的線程。后繼節(jié)點(diǎn)的決定必須同‘新入隊(duì)的節(jié)點(diǎn)在設(shè)置它的前驅(qū)節(jié)點(diǎn)的“next”屬性操作(即,新入隊(duì)節(jié)點(diǎn)為newNode,在newNode的前驅(qū)節(jié)點(diǎn)preNewNode進(jìn)行preNewNode.next = newNode操作)’產(chǎn)生競(jìng)爭(zhēng)。一個(gè)解決方法是必要的話當(dāng)一個(gè)節(jié)點(diǎn)的后繼看起來(lái)是空的時(shí)候,從原子更新“tail”向前檢測(cè)。(或者換句話說(shuō),next鏈接是一個(gè)優(yōu)化,所以我們通常不需要反向掃描。)
取消引入了對(duì)基本算法的一些保守性。當(dāng)我們必須為其他節(jié)點(diǎn)的取消輪詢時(shí),我們不需要留意一個(gè)取消的節(jié)點(diǎn)是在我們節(jié)點(diǎn)的前面還是后面。它的處理方式是總是根據(jù)取消的節(jié)點(diǎn)喚醒其后繼節(jié)點(diǎn),允許它們?nèi)ミB接到一個(gè)新的前驅(qū)節(jié)點(diǎn),除非我們能夠標(biāo)識(shí)一個(gè)未被取消的前驅(qū)節(jié)點(diǎn)來(lái)完成這個(gè)責(zé)任。
- waitStatus
volatile int waitStatus;
狀態(tài)屬性,只有如下值:
① SIGNAL:
static final int SIGNAL = -1;
這個(gè)節(jié)點(diǎn)的后繼(或者即將被阻塞)被阻塞(通過(guò)park阻塞)了,所以當(dāng)前節(jié)點(diǎn)需要喚醒它的后繼當(dāng)它被釋放或者取消時(shí)。為了避免競(jìng)爭(zhēng),獲取方法必須首先表示他們需要一個(gè)通知信號(hào),然后再原子性的嘗試獲取鎖,如果失敗,則阻塞。
也就是說(shuō),在獲取鎖的操作中,需要確保當(dāng)前node的preNode的waitStatus狀態(tài)值為’SIGNAL’,才可以被阻塞,當(dāng)獲取鎖失敗時(shí)。(『shouldParkAfterFailedAcquire』方法的用意就是這)
② CANCELLED:
static final int CANCELLED = 1;
這個(gè)節(jié)點(diǎn)由于超時(shí)或中斷被取消了。節(jié)點(diǎn)不會(huì)離開(kāi)(改變)這個(gè)狀態(tài)。尤其,一個(gè)被取消的線程不再會(huì)被阻塞了。
③ CONDITION:
static final int CONDITION = -2;
這個(gè)節(jié)點(diǎn)當(dāng)前在一個(gè)條件隊(duì)列中。它將不會(huì)被用于當(dāng)做一個(gè)同步隊(duì)列的節(jié)點(diǎn)直到它被轉(zhuǎn)移到同步隊(duì)列中,轉(zhuǎn)移的同時(shí)狀態(tài)值(waitStatus)將會(huì)被設(shè)置為0。(這里使用這個(gè)值將不會(huì)做任何事情與該字段其他值對(duì)比,只是為了簡(jiǎn)化機(jī)制)。
④ PROPAGATE:
static final int PROPAGATE = -3;
一個(gè)releaseShared操作必須被廣播給其他節(jié)點(diǎn)。(只有頭節(jié)點(diǎn)的)該值會(huì)在doReleaseShared方法中被設(shè)置去確保持續(xù)的廣播,即便其他操作的介入。
⑤ 0:不是上面的值的情況。
這個(gè)值使用數(shù)值排列以簡(jiǎn)化使用。非負(fù)的值表示該節(jié)點(diǎn)不需要信號(hào)(通知)。因此,大部分代碼不需要去檢查這個(gè)特殊的值,只是為了標(biāo)識(shí)。
對(duì)于常規(guī)的節(jié)點(diǎn)該字段會(huì)被初始化為0,競(jìng)爭(zhēng)節(jié)點(diǎn)該值為CONDITION。這個(gè)值使用CAS修改(或者可能的話,無(wú)競(jìng)爭(zhēng)的volatile寫(xiě))。
- prev
volatile Node prev
連接到前驅(qū)節(jié)點(diǎn),當(dāng)前節(jié)點(diǎn)/線程依賴與這個(gè)節(jié)點(diǎn)waitStatus的檢測(cè)。分配發(fā)生在入隊(duì)時(shí),并在出隊(duì)時(shí)清空(為了GC)。并且,一個(gè)前驅(qū)的取消,我們將短路當(dāng)發(fā)現(xiàn)一個(gè)未被取消的節(jié)點(diǎn)時(shí),未被取消的節(jié)點(diǎn)總是存在因?yàn)轭^節(jié)點(diǎn)不能被取消:只有在獲取鎖操作成功的情況下一個(gè)節(jié)點(diǎn)才會(huì)成為頭節(jié)點(diǎn)。一個(gè)被取消的線程絕不會(huì)獲取成功,一個(gè)線程只能被它自己取消,不能被其他線程取消。
- next
volatile Node next
連接到后繼的節(jié)點(diǎn),該節(jié)點(diǎn)是當(dāng)前的節(jié)點(diǎn)/線程釋放喚醒的節(jié)點(diǎn)。分配發(fā)生在入隊(duì)時(shí),在繞過(guò)取消的前驅(qū)節(jié)點(diǎn)時(shí)進(jìn)行調(diào)整,并在出隊(duì)列時(shí)清空(為了GC的緣故)。一個(gè)入隊(duì)操作(enq)不會(huì)被分配到前驅(qū)節(jié)點(diǎn)的next字段,直到tail成功指向當(dāng)前節(jié)點(diǎn)之后(通過(guò)CAS來(lái)將tail指向當(dāng)前節(jié)點(diǎn)?!篹nq』方法實(shí)現(xiàn)中,會(huì)先將node.prev = oldTailNode;在需要在CAS成功之后,即tail = node之后,再將oldTailNode.next = node;),所以當(dāng)看到next字段為null時(shí)并不意味著當(dāng)前節(jié)點(diǎn)是隊(duì)列的尾部了。無(wú)論如何,如果一個(gè)next字段顯示為null,我們能夠從隊(duì)列尾向前掃描進(jìn)行復(fù)核。被取消的節(jié)點(diǎn)的next字段會(huì)被設(shè)置為它自己,而不是一個(gè)null,這使得isOnSyncQueue方法更簡(jiǎn)單。
- thread
volatile Thread thread
這個(gè)節(jié)點(diǎn)的入隊(duì)線程。在構(gòu)建時(shí)初始化,在使用完后清除。
- nextWaiter
Node nextWaiter
鏈接下一個(gè)等待條件的節(jié)點(diǎn),或者一個(gè)指定的SHARED值。因?yàn)橹挥谐钟信潘i時(shí)能訪問(wèn)條件隊(duì)列,所以我們只需要一個(gè)簡(jiǎn)單的單鏈表來(lái)維持正在等待條件的節(jié)點(diǎn)。它們接下來(lái)會(huì)被轉(zhuǎn)換到隊(duì)列中以去重新獲取鎖。因?yàn)橹挥信潘i才有conditions,所以我們使用給一個(gè)特殊值保存的字段來(lái)表示共享模式。
也就是說(shuō),nextWaiter用于在排他鎖模式下表示正在等待條件的下一個(gè)節(jié)點(diǎn),因?yàn)橹挥信潘i模式有conditions;所以在共享鎖模式下,我們使用’SHARED’這個(gè)特殊值來(lái)表示該字段。
源碼分析
初始化
- 初始化 ———— 公平鎖:
ReentrantLock lock = new ReentrantLock(true)
- 初始化 ———— 非公平鎖:
ReentrantLock lock = new ReentrantLock()
或
ReentrantLock lock = new ReentrantLock(false)
lock
public void lock() {
sync.lock();
}
獲取鎖。
如果其他線程沒(méi)有持有鎖的話,獲取鎖并且立即返回,設(shè)置鎖被持有的次數(shù)為1.
如果當(dāng)前線程已經(jīng)持有鎖了,那么只有鎖的次數(shù)加1,并且方法立即返回。
如果其他線程持有了鎖,那么當(dāng)前線程會(huì)由于線程調(diào)度變得不可用,并處于休眠狀態(tài)直到當(dāng)前線程獲取到鎖,此時(shí)當(dāng)前線程持有鎖的次數(shù)被設(shè)置為1次。
-
公平鎖『lock()』方法的實(shí)現(xiàn):
『FairSync#lock()』
final void lock() {
acquire(1);
}
調(diào)用『acquire』在再次嘗試獲取鎖失敗的情況下,會(huì)將當(dāng)前線程入隊(duì)至等待隊(duì)列。該方法會(huì)在成功獲取鎖的情況下才會(huì)返回。因此該方法是可能導(dǎo)致阻塞的(線程掛起)。
-
非公平鎖『lock()』方法的實(shí)現(xiàn):
『NonfairSync#lock()』
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
① 嘗試獲取鎖,若『compareAndSetState(0, 1)』操作成功(這步操作有兩層意思。第一,當(dāng)前state為0,說(shuō)明當(dāng)前鎖沒(méi)有被任何線程持有;第二,原子性的將state從’0’修改為’1’成功,說(shuō)明當(dāng)前線程成功獲取了這個(gè)鎖),則說(shuō)明當(dāng)前線程成功獲取鎖。那么設(shè)置鎖的持有者為當(dāng)前線程(『setExclusiveOwnerThread(Thread.currentThread())』)。
那么此時(shí),AQS state為1,鎖的owner為當(dāng)前線程。結(jié)束方法。
② 如果獲取鎖失敗,則調(diào)用『acquire』在再次嘗試獲取鎖失敗的情況下,會(huì)將當(dāng)前線程入隊(duì)至等待隊(duì)列。該方法會(huì)在成功獲取鎖的情況下才會(huì)返回。因此該方法是可能導(dǎo)致阻塞的(線程掛起)。
- 公共方法『AbstractQueuedSynchronizer#acquire』
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
在排他模式下嘗試獲,忽略中斷。該方法的實(shí)現(xiàn)至少執(zhí)行一次『tryAcquire』方法,如果成功獲取鎖則返回。否則,線程將入隊(duì)等待隊(duì)列中,可能會(huì)重復(fù)阻塞和解除阻塞,以及調(diào)用『tryAcquire』直到成功獲取鎖。這個(gè)方法能被用于實(shí)現(xiàn)『Lock#lock』
① 執(zhí)行『tryAcquire』來(lái)嘗試獲取鎖,如果成功(即,返回true)。則返回true退出方法;否則到第②步
② 執(zhí)行『acquireQueued(addWaiter(Node.EXCLUSIVE), arg)』
『addWaiter(Node.EXCLUSIVE)』:為當(dāng)前線程創(chuàng)建一個(gè)排他模式的Node,并將這個(gè)節(jié)點(diǎn)加入等待隊(duì)列尾部。
『acquireQueued』:已經(jīng)入隊(duì)的節(jié)點(diǎn)排隊(duì)等待獲取鎖。
③ 如果在嘗試獲取鎖的過(guò)程中發(fā)現(xiàn)線程被標(biāo)志位了中斷。因?yàn)槭峭ㄟ^(guò)『Thread.interrupted()』方法來(lái)檢測(cè)的當(dāng)前線程是否有被標(biāo)志位中斷,該方法會(huì)清除中斷標(biāo)志,所以如果線程在嘗試獲取鎖的過(guò)程中發(fā)現(xiàn)被標(biāo)識(shí)為了中斷,則需要重新調(diào)用『Thread.currentThread().interrupt();』重新將中斷標(biāo)志置位。
該方法是排他模式下獲取鎖的方法,并且該方法忽略中斷,也就說(shuō)中斷不會(huì)導(dǎo)致該方法的結(jié)束。首先,會(huì)嘗試通過(guò)不公平的方式立即搶占該鎖(『tryAcquire』),如果獲取鎖成功,則結(jié)束方法。否則,將當(dāng)前線程加入到等待獲取鎖的隊(duì)列中,如果當(dāng)前線程還未入隊(duì)的話。此后就需要在隊(duì)列中排隊(duì)獲取鎖了,而這就不同于前面非公平的方式了,它會(huì)根據(jù)FIFO的公平方式來(lái)嘗試獲取這個(gè)鎖。而這個(gè)方法會(huì)一直“阻塞”直到成功獲取到鎖了才會(huì)返回。注意,這里的“阻塞”并不是指線程一直被掛起這,它可能被喚醒,然后同其他線程(比如,那么嘗試非公平獲取該鎖的線程)競(jìng)爭(zhēng)這個(gè)鎖,如果失敗,它會(huì)繼續(xù)被掛起,等待被喚醒,再重新嘗試獲取鎖,直到成功。
同時(shí)注意,關(guān)于中斷的操作。因?yàn)樵摲椒ㄊ遣豢芍袛嗟姆椒?,因此若在該方法的?zhí)行過(guò)程中線程被標(biāo)志位了中斷,我們需要確保這個(gè)標(biāo)志位不會(huì)因?yàn)榉椒ǖ恼{(diào)用而被清除,也就是我們不處理中斷,但是外層的邏輯可能會(huì)對(duì)中斷做相關(guān)的處理,我們不應(yīng)該影響中斷的狀態(tài),即,“私自”在不處理中斷的情況下將中斷標(biāo)志清除了。
先繼續(xù)來(lái)看公平鎖和非公平鎖對(duì)『tryAcquire』方法的實(shí)現(xiàn)
tryAcquire 這類型的方法都不會(huì)導(dǎo)致阻塞(即,線程掛起)。它會(huì)嘗試獲取鎖,如果失敗就返回false。
- FairSync#tryAcquire
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
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;
}
公平版本的『tryAcquire』方法。當(dāng)前線程沒(méi)有權(quán)限獲取鎖,除非遞歸調(diào)用到?jīng)]有等待者了,或者當(dāng)前線程就是第一個(gè)嘗試獲取鎖的線程(即,等待隊(duì)列中沒(méi)有等待獲取鎖的線程)。
① 獲取AQS state,即,當(dāng)前鎖被獲取的次數(shù)。如果為’0’,則說(shuō)明當(dāng)前鎖沒(méi)有被任何線程獲取,那么執(zhí)行『hasQueuedPredecessors』方法判斷當(dāng)前線程之前是否有比它等待更久準(zhǔn)備獲取鎖的線程:
a)如果有則方法結(jié)束,返回false;
b)如果沒(méi)有,則說(shuō)明當(dāng)前線程前面沒(méi)有另一個(gè)比它等待更久的時(shí)間在等待獲取這個(gè)鎖的線程,則嘗試通過(guò)CAS的方式讓當(dāng)前的線程獲取鎖。如果成功則設(shè)置持有鎖的線程為當(dāng)前線程(『setExclusiveOwnerThread(current)』),然后方法結(jié)束返回true。
② 如果AQS state > 0,則說(shuō)明當(dāng)前鎖已經(jīng)被某個(gè)線程所持有了,那么判斷這個(gè)持有鎖的線程是否就是當(dāng)前線程(『current == getExclusiveOwnerThread()』),如果是的話,嘗試進(jìn)行再次獲取這個(gè)鎖(ReentrantLock是一個(gè)可重入的鎖)如果獲取鎖的次數(shù)沒(méi)有超過(guò)上限的話(即,c + acquires > 0),則更新state的值為最終該鎖被當(dāng)前線程獲取的次數(shù),然后方法結(jié)束返回true;否則,如果當(dāng)前線程獲取這個(gè)鎖的次數(shù)超過(guò)了上限則或拋出Error異常。再者如果當(dāng)前線程不是持有鎖的線程,則方法結(jié)束返回false。
『AbstractQueuedSynchronizer#hasQueuedPredecessors』
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
查詢是否有線程已經(jīng)比當(dāng)前線程等待更長(zhǎng)的時(shí)間在等待獲取鎖。
這個(gè)方法的調(diào)用等價(jià)于(但更高效與):
『getFirstQueuedThread() != Thread.currentThread() && hasQueuedThreads()』
即,可見(jiàn)如下任一情況,則說(shuō)明當(dāng)前線程前面沒(méi)有比它等待更久的需要獲取鎖的線程:
a)當(dāng)隊(duì)列中第一個(gè)等待獲取鎖的線程是當(dāng)前線程時(shí)
b)等待隊(duì)列為空時(shí)。即,當(dāng)前沒(méi)有其他線程等待獲取鎖。
注意,因?yàn)橹袛嗪统瑫r(shí)導(dǎo)致的取消可能發(fā)生在任何時(shí)候,該方法返回‘true’不能保證其他線程會(huì)比當(dāng)前線程更早獲得到鎖。同樣,由于隊(duì)列是空的,在當(dāng)前方法返回‘false’之后,另一個(gè)線程可能會(huì)贏得一個(gè)入隊(duì)競(jìng)爭(zhēng)。
這個(gè)方法被涉及用于一個(gè)公平的同步器,以避免闖入。如果這個(gè)方法返回’true’,那么像是一個(gè)(公平)同步器的『tryAcquire』方法應(yīng)該返回’false’,以及『tryAcquireShared』方法需要返回一個(gè)負(fù)數(shù)值(除非這是一個(gè)可重入的鎖,因?yàn)榭芍厝腈i,獲取鎖的結(jié)果還需要判斷當(dāng)前線程是否就是已經(jīng)獲取鎖的線程了,如果是,則在沒(méi)有超過(guò)同一線程可獲取鎖的次數(shù)上限的情況下,當(dāng)前線程可以再次獲取這個(gè)鎖)。比如,一個(gè)公平的、可重入的、排他模式下的『tryAcquire』方法,可能看起來(lái)像是這樣的:

a)true:如果當(dāng)前線程前面有排隊(duì)等待的線程
b)false:如果當(dāng)前線程是第一個(gè)等待獲取鎖的線程(即,一般就是head.next);或者等待隊(duì)列為空。
該方法的正確性依賴于head在tail之前被初始化,以及head.next的精確性,如果當(dāng)前線程是隊(duì)列中第一個(gè)等待獲取鎖的線程的時(shí)候。
① tail節(jié)點(diǎn)的獲取一定先于head節(jié)點(diǎn)的獲取。因?yàn)閔ead節(jié)點(diǎn)的初始化在tail節(jié)點(diǎn)之前,那么基于當(dāng)前的tail值,你一定能獲取到有效的head值。這么做能保證接下來(lái)流程的正確性。舉個(gè)反例來(lái)說(shuō)明這么做的必要性:如果你按『Node h = head; Node t = tail;』的順序來(lái)對(duì)h、t進(jìn)行賦值,那么可能出現(xiàn)你在操作這兩步的時(shí)候有其他的線程正在執(zhí)行隊(duì)列的初始化操作,那么就可能的帶一個(gè)『h==null』,而『tail!=null』的情況(這種情況下,是不對(duì)的,因?yàn)閠ail!=null的話,head一定也不為null了),這使得『h != t』判斷為true,認(rèn)為當(dāng)下是一個(gè)非空的等待隊(duì)列,那么接著執(zhí)行『s = h.next』就會(huì)拋出NPE異常了。但是當(dāng)『Node t = tail; Node h = head;』按初始化相反的順序來(lái)賦值的話,則不會(huì)有問(wèn)題,因?yàn)槲覀儽WC了在tail取值的情況下,head的正確性。我們接下看下面的步驟,來(lái)說(shuō)明為什么這么做就可以了。
② 在獲取完t、h之后,我們接著先判斷『h != t』,該判斷的用意在于,判斷當(dāng)前的隊(duì)列是否為空。如果為true則說(shuō)明,當(dāng)前隊(duì)列非空。如果為false 則說(shuō)明當(dāng)前隊(duì)列為空,為空的話,方法就直接結(jié)束了,并返回false。
但是請(qǐng)注意,當(dāng)『h != t』為true時(shí),其實(shí)是有兩種情況的:
a)當(dāng)tail和head都非空時(shí),說(shuō)明此時(shí)等待隊(duì)列已經(jīng)完成了初始化,head和tail都指向了其隊(duì)列的頭和隊(duì)列的尾。
b)當(dāng)“tail==null”同時(shí)“head != null”,則說(shuō)明,此時(shí)隊(duì)列正在被其他線程初始化,當(dāng)前我們獲取的h、t是初始化未完成的中間狀態(tài)。但是沒(méi)關(guān)系,下面的流程會(huì)對(duì)此請(qǐng)進(jìn)行判斷。
③ 當(dāng)『h != t』返回’true’的話,繼續(xù)判斷『(s = h.next) == null || s.thread != Thread.currentThread()』。這里的兩個(gè)判斷分別對(duì)應(yīng)了兩種情況:
a)『(s = h.next) == null』返回’true’,則說(shuō)明當(dāng)獲取的h、t為初始化的中間狀態(tài),因?yàn)榈谝粋€(gè)線程入隊(duì)的時(shí)候,會(huì)先初始化隊(duì)列,然后才對(duì)head的next值進(jìn)行賦值,所以我們需要“s = h.next”是否為null進(jìn)行判斷,如果為’null’,則說(shuō)明當(dāng)前等待隊(duì)列正在被初始化,并且有一個(gè)線程正在入隊(duì)的操作中。所以此時(shí)方法直接結(jié)束,并且返回true。
b)如果『h != t』并且『(s = h.next) != null』,則說(shuō)明當(dāng)前線程已經(jīng)被初始化好了,并且等待隊(duì)列中的第一個(gè)等待獲取鎖的線程也已經(jīng)入隊(duì)了。那么接著我們就判斷這個(gè)在等待隊(duì)列中第一個(gè)等待獲取鎖的線程是不是當(dāng)前線程『s.thread != Thread.currentThread()』,如果是的話,方法結(jié)束并返回false,表示當(dāng)前線程前面沒(méi)有比它等待更久獲取這個(gè)鎖的線程了;否則方法結(jié)束返回true,表示當(dāng)前線程前面有比它等待更久希望獲取這個(gè)鎖的線程。
『AbstractQueuedSynchronizer#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;
}
根據(jù)給定的模式創(chuàng)建當(dāng)前線程的節(jié)點(diǎn),并將創(chuàng)建好的節(jié)點(diǎn)入隊(duì)(加入等待隊(duì)列尾部)。
首先在隊(duì)列非空的情況下會(huì)嘗試一次快速入隊(duì),也就是通過(guò)嘗試一次CAS操作入隊(duì),如果CAS操作失敗,則調(diào)用enq方法進(jìn)行“自旋+CAS”方法將創(chuàng)建好的節(jié)點(diǎn)加入隊(duì)列尾。
在排他模式下,將節(jié)點(diǎn)加入到鎖的同步隊(duì)列時(shí),Node的mode(即,waitStatus)為’EXCLUSIVE’。waitStatus是用于在排他鎖模式下當(dāng)節(jié)點(diǎn)處于條件隊(duì)列時(shí)表示下一個(gè)等待條件的節(jié)點(diǎn),所以在加入到鎖的同步隊(duì)列中(而非條件隊(duì)列),我們使用’EXCLUSIVE’這個(gè)特殊值來(lái)表示該字段。本文主要圍繞共享鎖模式的介紹,就不對(duì)其進(jìn)行展開(kāi)了,關(guān)于排他鎖的內(nèi)容會(huì)在“ReentrantLock源碼解析”一文中介紹。
- NonfairSync#tryAcquire
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
對(duì)父類AQS tryAcquire方法的重寫(xiě)。調(diào)用『nonfairTryAcquire(acquires)』方法,非公平的嘗試獲取這個(gè)可重入的排他鎖
『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;
}
執(zhí)行不公平的『tryLock』?!簍ryAcquire』在子類中實(shí)現(xiàn),但是都需要不公平的嘗試在『tryLock』方法中。
① 獲取state值,如果為’0’,則說(shuō)明當(dāng)前沒(méi)有線程占用鎖,那么調(diào)用CAS來(lái)嘗試將state的值從0修改為’acquires’的值,
a)如果成功則說(shuō)明當(dāng)前線程成功獲取到了這個(gè)不公平鎖,那么通過(guò)『setExclusiveOwnerThread(current)』方法來(lái)標(biāo)志當(dāng)前線程為持有鎖的線程,方法結(jié)束,返回true;
b)如果失敗,則說(shuō)明有其他線程先獲取到了這個(gè)鎖,那么當(dāng)前線程獲取鎖失敗。方法結(jié)束,返回false。
② "state != 0",則說(shuō)明當(dāng)前鎖已經(jīng)被某個(gè)線程所持有了,那么判斷當(dāng)前的線程是否就是持有鎖的那個(gè)線程(『if (current == getExclusiveOwnerThread())』)。
a)如果持有鎖的線程就是當(dāng)前線程,因?yàn)镽eentrantLock是一個(gè)可重入的鎖,所以接下來(lái)繼續(xù)判斷預(yù)計(jì)遞歸獲取鎖的次數(shù)是否超過(guò)了限制的值(即,“nextc < 0”則說(shuō)明預(yù)計(jì)遞歸獲取鎖的次數(shù)超過(guò)了限制值Integer.MAX_VALUE了),那么會(huì)拋出“Error”異常;否則將當(dāng)前state的值設(shè)置為最新獲取鎖的次數(shù)(注意,這里不需要使用CAS的方式來(lái)修改state了,因?yàn)槟懿僮鞯竭@里的一定就是當(dāng)前持有鎖的線程了,因此是不會(huì)發(fā)送多線程競(jìng)爭(zhēng)的情況)。然后方法結(jié)束,返回true;
b)如果持有鎖的線程不是當(dāng)前線程,那么當(dāng)前線程獲取鎖失敗。方法結(jié)束,返回false。
-
『FairSync#lock』 VS 『NonfairSync#lock』
a)在公平鎖的模式下,所有獲取鎖的線程必須是按照調(diào)用lock方法先后順序來(lái)決定的,嚴(yán)格的說(shuō)當(dāng)有多個(gè)線程同時(shí)嘗試獲取同一個(gè)鎖時(shí),多個(gè)線程最終獲取鎖的先后順序是由入隊(duì)等待隊(duì)列的順序決定的,當(dāng)然,第一個(gè)獲取鎖的線程是無(wú)需入隊(duì)的,等待隊(duì)列是用于存儲(chǔ)那些嘗試獲取鎖失敗后的節(jié)點(diǎn)。并且按照FIFO的順序讓隊(duì)列中的節(jié)點(diǎn)依次獲取鎖。
b)在非公平模式下,當(dāng)執(zhí)行l(wèi)ock時(shí),無(wú)論當(dāng)前等待隊(duì)列中是否有等待獲取鎖的線程了,當(dāng)前線程都會(huì)嘗試直接去獲取鎖。
??兩點(diǎn)從『FairSync#lock』與『NonfairSync#lock』 實(shí)現(xiàn)的不同,以及『FairSync#tryAcquire』與『NonfairSync#tryAcquire』方法實(shí)現(xiàn)的不同中都能表現(xiàn)出來(lái)。
對(duì)于非公平鎖:首先會(huì)嘗試立即搶占獲取鎖(若鎖當(dāng)前沒(méi)有被任何線程持有時(shí),并且此時(shí)它會(huì)和當(dāng)前同時(shí)首次嘗試獲取該鎖的線程以及等待隊(duì)列中嘗試獲取該鎖的線程競(jìng)爭(zhēng)),如果獲取鎖失敗,則會(huì)被入隊(duì)到等待隊(duì)列中,此時(shí)就需要排隊(duì)等待獲取鎖了。
在排他鎖模式下,等待隊(duì)列中的第一個(gè)等待獲取鎖的線程(即,前驅(qū)節(jié)點(diǎn)是head節(jié)點(diǎn)的節(jié)點(diǎn)),僅代表這個(gè)節(jié)點(diǎn)當(dāng)前有競(jìng)爭(zhēng)獲取鎖的權(quán)力,并不代表它會(huì)成功獲取鎖。因?yàn)樗赡軙?huì)同非公平獲取鎖的操作產(chǎn)生競(jìng)爭(zhēng)。
繼續(xù)回到『AbstractQueuedSynchronizer#acquire』方法,繼續(xù)展開(kāi)里面的實(shí)現(xiàn)。我們接著看『acquireQueued』方法是如何實(shí)現(xiàn)將已經(jīng)入隊(duì)的節(jié)點(diǎn)排隊(duì)等待獲取鎖的。
『AbstractQueuedSynchronizer#acquireQueued』
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);
}
}
用于已經(jīng)入隊(duì)的線程在排他不可中斷模式下獲取鎖。同時(shí)也被用于給條件(condition)的wait方法來(lái)獲取鎖。
該方法不會(huì)因?yàn)橹袛嗟陌l(fā)送而返回,只有在獲取鎖的情況下才會(huì)返回,但是如果在等待獲取鎖的過(guò)程中,當(dāng)前線程被標(biāo)識(shí)為了中斷,則在方法返回的時(shí)候返回true,否則方法返回是返回false。
① 獲取當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn),如果前驅(qū)節(jié)點(diǎn)是head節(jié)點(diǎn),則說(shuō)明當(dāng)前節(jié)點(diǎn)是隊(duì)列中第一個(gè)等待獲取鎖的節(jié)點(diǎn),那么就執(zhí)行『tryAcquire』方法嘗試獲取排他鎖,如果獲取排他鎖成功(即,tryAcquire方法返回true)則調(diào)用『setHead(node)』將當(dāng)前節(jié)點(diǎn)設(shè)置為頭節(jié)點(diǎn)。然后將p(即,舊的head節(jié)點(diǎn))的next置null,有助于p被垃圾收集器收集。然后標(biāo)識(shí)failed為false。結(jié)束方法調(diào)用,返回interrupted(該字段表示在等待獲取鎖的過(guò)程中,當(dāng)前線程是否有被表示為中斷了)。
② 如果當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)不是head節(jié)點(diǎn),說(shuō)明該節(jié)點(diǎn)前面已經(jīng)有等待著獲取這個(gè)排他鎖的節(jié)點(diǎn);或者當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)是head節(jié)點(diǎn),但是當(dāng)前節(jié)點(diǎn)獲取鎖失敗了,那么執(zhí)行『shouldParkAfterFailedAcquire』方法,若該方法返回true,則說(shuō)明本次獲取排他鎖失敗需要阻塞/掛起當(dāng)前線程,那么就調(diào)用『LockSupport.park(this);』將當(dāng)前線程掛起,直到被喚醒,并且若掛起期間該線程被標(biāo)志為了中斷狀態(tài),則將interrupted標(biāo)識(shí)為true。
③ 當(dāng)當(dāng)前節(jié)點(diǎn)經(jīng)過(guò)多次喚醒與掛起,終于成功獲取鎖后,則退出方法,并返回當(dāng)前線程是否有被中斷的標(biāo)志。如果當(dāng)前節(jié)點(diǎn)因?yàn)槟承┰驔](méi)有成功獲取到鎖,卻要結(jié)束該方法了,那么調(diào)用『cancelAcquire(node)』方法將當(dāng)前節(jié)點(diǎn)從等待隊(duì)列中移除。因?yàn)榉椒ńY(jié)束了,說(shuō)明當(dāng)前節(jié)點(diǎn)不會(huì)被操作再去嘗試獲取鎖了,那么就不應(yīng)該作為一個(gè)有效節(jié)點(diǎn)放在等待隊(duì)列中,應(yīng)該被標(biāo)識(shí)為無(wú)效的節(jié)點(diǎn)后從隊(duì)列中移除。
『AbstractQueuedSynchronizer#setHead』
private void setHead(Node node) {
head = node;
node.thread = null;
node.prev = null;
}
將node設(shè)置為隊(duì)列的頭節(jié)點(diǎn),當(dāng)它出隊(duì)時(shí)。該方法只能被獲取方法調(diào)用。僅將無(wú)用字段清空(即,置為null)以便于GC并廢除不必要的通知和遞歸。
『AbstractQueuedSynchronizer#shouldParkAfterFailedAcquire』
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus must be 0 or PROPAGATE. Indicate that we
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
檢查并修改一個(gè)節(jié)點(diǎn)的狀態(tài),當(dāng)該節(jié)點(diǎn)獲取鎖失敗時(shí)。返回true如果線程需要阻塞。這是主要的信號(hào)通知控制在所有的獲取鎖循環(huán)中。要求’pred’ == ‘node.prev’
① 如果pred.waitStatus == Node.SIGNAL。則說(shuō)明node的前驅(qū)節(jié)點(diǎn)已經(jīng)被要求去通知釋放它的后繼節(jié)點(diǎn),所以node可以安全的被掛起(park)。然后,退出方法,返回true。
② 如果pred.waitStatus > 0。則說(shuō)明node的前驅(qū)節(jié)點(diǎn)被取消了。那么跳過(guò)這個(gè)前驅(qū)節(jié)點(diǎn)并重新標(biāo)志一個(gè)有效的前驅(qū)節(jié)點(diǎn)(即,waitStatus <= 0 的節(jié)點(diǎn)可作為有效的前驅(qū)節(jié)點(diǎn)),然后,退出方法,返回false。
③ 其他情況下,即pred.waitStatus為’0’或’PROPAGATE’。表示我們需要一個(gè)通知信號(hào)(即,當(dāng)前的node需要喚醒的通知),但是當(dāng)前還不能掛起node。調(diào)用『compareAndSetWaitStatus(pred, ws, Node.SIGNAL)』方法通過(guò)CAS的方式來(lái)修改前驅(qū)節(jié)點(diǎn)的waitStatus為“SIGNAL”。退出方法,返回false。
我們需要一個(gè)通知信號(hào),主要是因?yàn)楫?dāng)前線程要被掛起了(park)。而如果waitStatus已經(jīng)是’SIGNAL’的話就無(wú)需修改,直接掛起就好,而如果waitStatus是’CANCELLED’的話,說(shuō)明prev已經(jīng)被取消了,是個(gè)無(wú)效節(jié)點(diǎn)了,那么無(wú)需修改這個(gè)無(wú)效節(jié)點(diǎn)的waitStatus,而是需要先找到一個(gè)有效的prev。因此,剩下的情況就只有當(dāng)waitStatus為’0’和’PROPAGAET’了(注意,waitStatus為’CONDITION’是節(jié)點(diǎn)不在等待隊(duì)列中,所以當(dāng)下情況waitStatus不可能為’CONDITION’),這是我們需要將prev的waitStatus使用CAS的方式修改為’SIGNAL’,而且只有修改成功的情況下,當(dāng)前的線程才能安全被掛起。
還值得注意的時(shí),因此該方法的CAS操作都是沒(méi)有自旋的,所以當(dāng)它操作完CAS后都會(huì)返回false,在外層的方法中會(huì)使用自旋,當(dāng)發(fā)現(xiàn)返回的是false時(shí),會(huì)再次調(diào)用該方法,以檢查保證有當(dāng)前node有一個(gè)有效的prev,并且其waitStatus為’SIGNAL’,在此情況下當(dāng)前的線程才會(huì)被掛起(park)。
unlock
public void unlock() {
sync.release(1);
}
嘗試去釋放這個(gè)鎖。
如果當(dāng)前線程是持有這個(gè)鎖的線程,那么將持有次數(shù)減少1。如果釋放后當(dāng)前的鎖被持有的次數(shù)為0,那么鎖被釋放。如果當(dāng)前線程不是持有鎖的線程,那么拋出“IllegalMonitorStateException”異常。
『AbstractQueuedSynchronizer#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;
}
排他模式下的釋放。該方法的實(shí)現(xiàn)通過(guò)解除一個(gè)或多個(gè)線程的的阻塞,如果『tryRelase』方法返回true的話。
該方法能夠被用于實(shí)現(xiàn)『Lock#unlock』。
① 調(diào)用『tryRelease(arg)』方法來(lái)嘗試設(shè)置狀態(tài)值來(lái)反映一個(gè)排他模式下的釋放。如果操作成功,則進(jìn)入步驟[2];否則,如果操作失敗,則方法結(jié)束,返回false。
② 在成功釋放給定的狀態(tài)值后,獲取等待隊(duì)列的頭節(jié)點(diǎn)。如果頭節(jié)點(diǎn)不為null并且頭節(jié)點(diǎn)的waitStatus!=0(頭節(jié)點(diǎn)的waitStatus要么是’0’,要么是’SIGNAL’),那么執(zhí)行『unparkSuccessor(h)』來(lái)喚醒頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)。(節(jié)點(diǎn)被喚醒后,會(huì)繼續(xù)acquireQueued方法中流程)
③ 只要『tryRelease(arg)』釋放操作成功,無(wú)論是否需要喚醒頭結(jié)點(diǎn)的后繼節(jié)點(diǎn),方法結(jié)束都會(huì)返回true。
『tryRelease』
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
嘗試通過(guò)設(shè)置狀態(tài)值來(lái)反映一個(gè)在排他模式下的釋放操作。
①『Thread.currentThread() != getExclusiveOwnerThread()』如果執(zhí)行釋放操作的線程不是持有鎖的線程,那么直接拋出“IllegalMonitorStateException”異常,方法結(jié)束;否則
② 如果執(zhí)行當(dāng)前釋放操作的線程是持有鎖的線程,那么
a)計(jì)算新state的值,即當(dāng)前釋放操作后state的值,如果state為0,則說(shuō)明當(dāng)前線程完成釋放了對(duì)該鎖的持有,那么將鎖的持有者重置為null(即,『setExclusiveOwnerThread(null)』)。然后通過(guò)『setState(c);』將AQS的state值設(shè)置為這個(gè)新的state值(即,0),結(jié)束方法,返回true,表示該鎖現(xiàn)在沒(méi)有線程持有,可以被重新獲取。
b)如果新state的值不為0(即,大于0),則說(shuō)明當(dāng)前的線程并未完成釋放該鎖(因?yàn)閞eentrantLock是一個(gè)可重入的鎖,所以一個(gè)線程可以多次獲取這鎖,而state值就表示這一線程獲取鎖的次數(shù)),那么通過(guò)『setState(c);』將AQS的state值設(shè)置為這個(gè)新的state值,結(jié)束方法,返回false。
可見(jiàn)對(duì)于『tryRelease』方法,釋放鎖操作失敗是通過(guò)拋出“IllegalMonitorStateException”異常來(lái)表示的。該方法無(wú)論返回‘true’還是‘false’都表示本次的釋放操作完成了。返回‘true’表示的是鎖已經(jīng)被當(dāng)前線程完全釋放了,其他線程可以繼續(xù)爭(zhēng)奪這個(gè)鎖了,在完全釋放鎖的時(shí)候也會(huì)將鎖中持有者字段重新置null;返回‘false’表示的是當(dāng)前釋放操作完成后,該線程還繼續(xù)持有這該鎖,此時(shí)其他線程是無(wú)法獲取到這個(gè)鎖的。
同時(shí),我們可以知道,釋放操作只能有持有鎖的線程來(lái)完成,因此對(duì)于AQS state字段(一個(gè)volatile字段)的修改,不需要使用CAS來(lái)完成,只需要直接設(shè)置修改就好。
『AbstractQueuedSynchronizer#unparkSuccessor』
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);
}
喚醒后繼節(jié)點(diǎn),如果存在的話
① 如果狀態(tài)值是負(fù)數(shù),則在預(yù)期發(fā)信號(hào)通知時(shí)清除這個(gè)負(fù)數(shù)狀態(tài)值。如果狀態(tài)被等待的線程修改了或者清除負(fù)數(shù)狀態(tài)值失敗是允許。
② 后繼節(jié)點(diǎn)的線程被喚醒,后繼節(jié)點(diǎn)通常就是下一個(gè)節(jié)點(diǎn)。但是如果下一個(gè)節(jié)點(diǎn)被取消了或者下一個(gè)節(jié)點(diǎn)為null,則從隊(duì)列尾(tail)往前遍歷去找真實(shí)的未取消的后繼節(jié)點(diǎn)。
『(s == null || s.waitStatus > 0)』:說(shuō)明下一個(gè)節(jié)點(diǎn)為null或被取消了(waitStatus允許的狀態(tài)值中,只有’CANCELLED’是>0的)。那么,就從隊(duì)列尾(tail)開(kāi)始向前遍歷,獲取第一個(gè)非空且未被取消的節(jié)點(diǎn)。如果存在這樣的一個(gè)后繼節(jié)點(diǎn)的話(即,“s != null”),則執(zhí)行『LockSupport.unpark(s.thread);』操作來(lái)喚醒這個(gè)節(jié)點(diǎn)的線程,此時(shí)等待隊(duì)列中第一個(gè)等待的線程就會(huì)被重新啟動(dòng),流程會(huì)回到『acquireQueued』方法,該線程會(huì)重新重試獲取該鎖,如果成功acquireQueued方法返回,否則線程會(huì)再次被掛起,等待下次喚醒后再去再去競(jìng)爭(zhēng)獲取鎖。
Q:關(guān)于node的waitStatus為’CANCELLED’的情況?
A:關(guān)于node的waitStatus為’CANCELLED’的情況:比如,當(dāng)這個(gè)node被中斷了,或者設(shè)置的超時(shí)時(shí)間到了,那么說(shuō)明這個(gè)線程獲取鎖失敗,那么此時(shí)就應(yīng)該將其設(shè)置為cancelled,因?yàn)槿绻摼€程還需要獲取鎖的話,會(huì)重新調(diào)用獲取鎖的方法,而獲取鎖的方法就是創(chuàng)建一個(gè)新的node的。所以,那么線程獲取鎖失敗的時(shí)候就會(huì)將這個(gè)node的waitStatus設(shè)置為’CANCELLED’,一個(gè)被取消的線程絕不會(huì)獲取鎖成功,一個(gè)線程只能被它自己取消,不能被其他線程取消。
Q:關(guān)于node為null的情況?
A:關(guān)于node為null的情況:比如,一個(gè)入隊(duì)操作(enq)不會(huì)被分配到前驅(qū)節(jié)點(diǎn)的next字段,直到tail成功指向當(dāng)前節(jié)點(diǎn)之后(通過(guò)CAS來(lái)將tail指向當(dāng)前節(jié)點(diǎn)?!篹nq』方法實(shí)現(xiàn)中,會(huì)先將node.prev = oldTailNode;在需要在CAS成功之后,即tail = node之后,再將oldTailNode.next = node;),所以當(dāng)看到next字段為null時(shí)并不意味著當(dāng)前節(jié)點(diǎn)是隊(duì)列的尾部了。無(wú)論如何,如果一個(gè)next字段顯示為null,我們能夠從隊(duì)列尾向前掃描進(jìn)行復(fù)核。
Q:對(duì)于ReentrantLock無(wú)論是公平鎖還是非公平鎖,在入隊(duì)時(shí)waitStatus都是什么??
能確定的是從條件等待隊(duì)列轉(zhuǎn)移到鎖的同步隊(duì)列的時(shí)候,節(jié)點(diǎn)的waitStatus是’0’。
A:無(wú)論是公平鎖還是非公平鎖,在構(gòu)建一個(gè)node的時(shí)候,waitStatus都是默認(rèn)值’0’。然后在將node入隊(duì)到鎖的等待隊(duì)列中后就會(huì)執(zhí)行『acquireQueued』來(lái)等待獲取鎖,而該方法會(huì)修改當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)的waitStatus(即,『shouldParkAfterFailedAcquire(p, node)』方法)。在當(dāng)前節(jié)點(diǎn)無(wú)法獲取鎖的時(shí)候需要被掛起前會(huì)將其前驅(qū)節(jié)點(diǎn)的waitStatus設(shè)置為’Node.SIGNAL’。這樣在釋放操作中(『release』),如果釋放后發(fā)現(xiàn)鎖的state為’0’,則說(shuō)明鎖當(dāng)前可以被其他線程獲取了,那么就會(huì)獲取鎖的等待隊(duì)列的head節(jié)點(diǎn),如果head節(jié)點(diǎn)的waitStatus!=0(即,head的waitStatus為’Node.SIGNAL’或’Node.PROPAGATE’,其中’Node.PROPAGATE’是共享模式下head節(jié)點(diǎn)的waitStatus可能的值,在排他模式下,head節(jié)點(diǎn)的waitStatus是’Node.SIGNAL’或’0’),那么說(shuō)明head節(jié)點(diǎn)后面有等待喚醒獲取鎖的線程,那么調(diào)用『unparkSuccessor』方法來(lái)喚醒head節(jié)點(diǎn)的后繼節(jié)點(diǎn)。
在排他鎖模式下,head節(jié)點(diǎn)的waitStatus不是在該節(jié)點(diǎn)被設(shè)置為head節(jié)點(diǎn)的時(shí)候修改的。而是如果有節(jié)點(diǎn)入隊(duì)到等待隊(duì)列中,并且此時(shí)該節(jié)點(diǎn)無(wú)法獲取鎖,那么會(huì)將其前驅(qū)節(jié)點(diǎn)的waitStatus設(shè)置為’Node.SIGNAL’后,該節(jié)點(diǎn)對(duì)應(yīng)的線程就被掛起了。所以也就是說(shuō),如果head節(jié)點(diǎn)后還有節(jié)點(diǎn)等待獲取鎖,那么此時(shí)head節(jié)點(diǎn)的waitStatus自然會(huì)使’Node.SIGNAL’,這是在head節(jié)點(diǎn)的后繼節(jié)點(diǎn)入隊(duì)后等待獲取鎖的過(guò)程中設(shè)置的。而將一個(gè)節(jié)點(diǎn)設(shè)置為head節(jié)點(diǎn),僅是將該節(jié)點(diǎn)賦值給head節(jié)點(diǎn),并將thread和prev屬性會(huì)被置null。
ConditionObject ———— 條件對(duì)象
條件對(duì)象用來(lái)管理那些已經(jīng)獲得了一個(gè)鎖,但是卻不能做有用工作的線程。
Condition實(shí)現(xiàn)是AbstractQueuedSynchronizer作為一個(gè)Lock實(shí)現(xiàn)的基礎(chǔ)。
該類的方法文檔描述了機(jī)制,而不是從鎖和條件(Condition)用戶的觀點(diǎn)來(lái)指定行為規(guī)范。該類所暴露的版本通常需要伴隨著依賴于描述相關(guān)AbstractQueuedSynchronizer的條件語(yǔ)義的文檔。
這個(gè)類是可序列化的,但是所有字段都是transient的,所以反序列化的conditions沒(méi)有等待者。
注意,關(guān)于在ConditionObject中的描述,若無(wú)特殊說(shuō)明“等待隊(duì)列”均指“條件等待隊(duì)列”,同鎖的等待隊(duì)列不同!
條件等待隊(duì)列中有效節(jié)點(diǎn)的waitStatus只能是“Node.CONDITION”,這說(shuō)明,如果發(fā)現(xiàn)條件等待隊(duì)列中的節(jié)點(diǎn)waitStatus!=“Node.CONDITION”,則說(shuō)明這個(gè)節(jié)點(diǎn)被取消等待條件了,那么應(yīng)該將其出條件等待隊(duì)列中移除。
// 等待隊(duì)列的頭節(jié)點(diǎn)
private transient Node firstWaiter;
// 等待隊(duì)列的尾節(jié)點(diǎn)
private transient Node lastWaiter;
- 條件對(duì)象初始化
Condition condition = lock.newCondition()
『newCondition』
public Condition newCondition() {
return sync.newCondition();
}
返回一個(gè)用于這個(gè)Lock實(shí)例的Condition實(shí)例。
返回的Condition實(shí)例與內(nèi)置的監(jiān)視器鎖一起使用時(shí),支持同Object監(jiān)控器方法(『Object#wait()』、『Object#notify』、『Object#notifyAll』)相同的用法。
如果這個(gè)鎖沒(méi)有被持有的話任何Condition等待(『Condition#await()』)或者通知(『Condition#signal』)方法被調(diào)用,那么一個(gè)“IllegalMonitorStateException”異常將會(huì)拋出。(也就說(shuō)是說(shuō),Condition是用在鎖已經(jīng)被持有的情況下)
當(dāng)一個(gè)condition的等待方法被調(diào)用(『Condition#await()』),鎖會(huì)被釋放,并且在這個(gè)方法返回之前,鎖會(huì)被重新獲取并且鎖的持有次數(shù)會(huì)重新存儲(chǔ)為這個(gè)方法被調(diào)用到時(shí)候的值。
如果一個(gè)線程在等待期間被中斷了,那么等待將會(huì)結(jié)束,一個(gè)“InterruptedException”異常將會(huì)拋出,并且線程的中斷狀態(tài)將被清除。
等待線程被以先進(jìn)先出(FIFO)的順序被通知。
等待獲得鎖的線程和調(diào)用await方法的線程存在本質(zhì)上的不同。一旦一個(gè)線程調(diào)用await方法,它進(jìn)入該條件的等待集。當(dāng)鎖可用時(shí),該線程不能馬上解除阻塞。相反,它處于阻塞狀態(tài),直到另一個(gè)線程調(diào)用同一條件上的signalAll方法時(shí)為止。這一調(diào)用重新激活因?yàn)檫@一條件而等待的所有線程。當(dāng)這些線程從等待集當(dāng)中移出時(shí),它們?cè)俅纬蔀榭蛇\(yùn)行的,調(diào)度器將再次激活它們。同時(shí),它們將試圖重新進(jìn)入該對(duì)象。一旦鎖成為可用的,它們中的某個(gè)將從await調(diào)用返回,獲得該鎖并從被阻塞的地方繼續(xù)執(zhí)行。
-
await
『AbstractQueuedSynchronizer#await』
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
實(shí)現(xiàn)可中斷的條件等待。
1.如果當(dāng)前線程被中斷了,則拋出“InterruptedException”異常。
2.保存通過(guò)『getState』返回的鎖的狀態(tài)(state)
3.調(diào)用『release』方法,使用保存的state作為參數(shù),如果該方法失敗則拋出“IllegalMonitorStateException”異常
4.線程阻塞直到收到喚醒通知或者線程被中斷
5.通過(guò)調(diào)用使用保存狀態(tài)為參數(shù)的指定版本的『acquire』方法來(lái)重新獲取鎖。
6.如果在步驟4中線程在堵塞的時(shí)候被中斷了,那么拋出“InterruptedException”異常。
① 首先一進(jìn)入該方法會(huì)先判斷當(dāng)前線程是否被標(biāo)志為了中斷狀態(tài),如果是則拋出“InterruptedException”異常,并且中斷狀態(tài)被清除。
② 調(diào)用『addConditionWaiter』添加一個(gè)新的等待節(jié)點(diǎn)到條件等待隊(duì)列中。
③ 調(diào)用『fullyRelease(node)』使用鎖當(dāng)前的狀態(tài)值執(zhí)行釋放,并返回這個(gè)狀態(tài)值。(即,該方法調(diào)用完后,并執(zhí)行成功的話,那么此時(shí)其他線程可以去獲取這個(gè)鎖了,可見(jiàn)await方法會(huì)使當(dāng)前線程放棄對(duì)鎖的持有。同時(shí)返回鎖在釋放前的狀態(tài)值。)
④ 自旋判斷創(chuàng)建的等待節(jié)點(diǎn)是否在所的同步隊(duì)列中了,如果沒(méi)有(則說(shuō)明節(jié)點(diǎn)還未被信號(hào)通知,以從條件等待隊(duì)列中轉(zhuǎn)移到鎖的同步隊(duì)列中),則執(zhí)行『LockSupport.park(this);』掛起當(dāng)前線程,線程會(huì)一直被掛起直到被信號(hào)通知喚醒(『signal()』或『signalAll()』方法會(huì)將節(jié)點(diǎn)從條件等待隊(duì)列轉(zhuǎn)移到鎖的同步隊(duì)列中,并根據(jù)加入到同步隊(duì)列后得到的前驅(qū)節(jié)點(diǎn)的waitStatus,可能會(huì)去喚醒當(dāng)前線程;或者當(dāng)鎖的同步等待隊(duì)列中的線程依次獲取鎖并釋放后,直到輪到當(dāng)前線程成為同步隊(duì)列中第一個(gè)等待獲取鎖的線程時(shí),當(dāng)前線程或被喚醒)。接著判斷線程是否發(fā)生中斷,如果發(fā)送中斷,則退出自旋;否則繼續(xù)自旋重新執(zhí)行本步驟的流程,直至新創(chuàng)建的等待節(jié)點(diǎn)被轉(zhuǎn)移到鎖的同步隊(duì)列中。
⑤ 執(zhí)行『acquireQueued(node, savedState)』方法在排他模式下從等待隊(duì)列中的線程獲取鎖,并且在獲取鎖后將鎖的狀態(tài)值設(shè)置為給定’savedState’(由于’savedState’就是上面因條件等待而釋放鎖前該線程獲取鎖的次數(shù),而在該線程重新獲取鎖,繼續(xù)await之后的流程時(shí),保持了該線程在await之前持有鎖的狀態(tài))。并且該方法會(huì)在獲取鎖的情況下才會(huì)返回:
a)若在等待獲取鎖的過(guò)程中,當(dāng)前線程被標(biāo)識(shí)為了中斷,則在方法返回的時(shí)候返回true;接著判斷interruptMode是否等于“THROW_IE”,如果為true,則說(shuō)明節(jié)點(diǎn)的等待在得到喚醒通知之前就被取消了,此時(shí)interruptMode為“THROW_IE”;否則interruptMode!=THROW_IE,則說(shuō)明節(jié)點(diǎn)的等待在得到喚醒通知之后才被取消了,那么設(shè)置interruptMode為“REINTERRUPT”,繼續(xù)步驟[6]
b)若在等待獲取鎖的過(guò)程中,當(dāng)前線程未被標(biāo)識(shí)為中斷,則繼續(xù)步驟[6]
(這里一個(gè)正常的未被中斷的流程就是,await的節(jié)點(diǎn)對(duì)應(yīng)的線程會(huì)在步驟[4]被掛起,然后在某一個(gè)時(shí)刻因?yàn)閟ignalAll()方法調(diào)用,該節(jié)點(diǎn)被轉(zhuǎn)移到了鎖的等待隊(duì)列中。然后當(dāng)該線程為鎖的等待隊(duì)列中第一個(gè)等待獲取鎖的線程時(shí),會(huì)被它的前驅(qū)節(jié)點(diǎn)喚醒,此時(shí)節(jié)點(diǎn)被喚醒,判斷得到已經(jīng)在等待隊(duì)列中了,那么結(jié)束步驟[4]的自旋,進(jìn)入的步驟[5],調(diào)用『acquireQueued(node, savedState)』嘗試獲取鎖,此時(shí)節(jié)點(diǎn)已經(jīng)具有獲取鎖的權(quán)限了,如果成功獲取鎖流程繼續(xù),否則節(jié)點(diǎn)會(huì)被再次掛起,acquireQueued方法會(huì)阻塞直到當(dāng)前線程獲取鎖的情況下返回。)
⑥ 如果node節(jié)點(diǎn)的nextWaiter非null,那么執(zhí)行『unlinkCancelledWaiters();』來(lái)清除等待隊(duì)列中被取消的節(jié)點(diǎn)。
因?yàn)?,如果node節(jié)點(diǎn)是通過(guò)signal/signalAll信號(hào)通知而從條件等待隊(duì)列轉(zhuǎn)移到鎖的同步隊(duì)列的話,那么node的nextWaiter是為null(在signal/signalAll方法中會(huì)將該字段置為null);否則如果是因?yàn)橹袛喽鴮⒐?jié)點(diǎn)從條件等待隊(duì)列轉(zhuǎn)移到鎖的同步隊(duì)列的話,此時(shí)nextWaiter是不會(huì)被重置的,它依舊指向該節(jié)點(diǎn)在條件等待隊(duì)列中的下一個(gè)節(jié)點(diǎn)。
⑦ 如果中斷模式標(biāo)志不為’0’(即,“interruptMode != 0”),則根據(jù)給定的中斷模式(interruptMode)在等待結(jié)束后報(bào)告中斷(『reportInterruptAfterWait(interruptMode)』)
因此,從『await()』方法中,我們可以得知下面幾點(diǎn):
- 創(chuàng)建一個(gè)條件等待節(jié)點(diǎn),并加入條件等待隊(duì)列尾部。
- 徹底釋放當(dāng)前線程所持有鎖(因?yàn)?,首先只有在持有鎖的情況下才可以執(zhí)行await操作,再者ReentrantLock是一個(gè)可重入的鎖,因此同一個(gè)線程可以多次獲取鎖),這樣鎖就可以被其他線程獲取。但會(huì)記錄這個(gè)線程在徹底釋放鎖之前持有該鎖的次數(shù)(即,鎖的state值)
- 在該線程再次獲取該鎖時(shí),會(huì)將鎖的state設(shè)置為釋放之前的值。即,從await()條件等待返回的時(shí)候,當(dāng)前線程對(duì)鎖持有的狀態(tài)同await()等待條件之前是一致的。
- 節(jié)點(diǎn)從條件等待隊(duì)列中轉(zhuǎn)移到鎖的同步隊(duì)列是兩種情況:
a)收到了信號(hào)通知,即signal/signalAll
b)在未收到信號(hào)通知之前,檢測(cè)到了當(dāng)前線程被中斷的標(biāo)志。 - 在當(dāng)前線程重新獲取到鎖,準(zhǔn)備從await方法返回的時(shí)候,await方法的返回也分兩種情況:
a)在條件等待中的節(jié)點(diǎn)是通過(guò)signal/signalAll信號(hào)通知轉(zhuǎn)移到鎖的同步隊(duì)列的,然后再在同步隊(duì)列中根據(jù)FIFO的順序來(lái)重新獲取到了該鎖。那么此時(shí)await方法正常返回。(在信號(hào)通知之后線程可能被標(biāo)志位中斷,但這不影響方法的正常返回)
b)在條件等待中節(jié)點(diǎn)是因?yàn)楫?dāng)前線程被標(biāo)志為了中斷而將其轉(zhuǎn)移到了鎖的同步隊(duì)列中,這樣在當(dāng)前線程再次重新獲取鎖時(shí),方法會(huì)異常返回,即拋出“InterruptedException”異常。
接下來(lái)對(duì),『await』中的源碼細(xì)節(jié)進(jìn)一步展開(kāi)
『AbstractQueuedSynchronizer#addConditionWaiter』
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
Node node = new Node(Thread.currentThread(), Node.CONDITION);
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
添加一個(gè)新的等待者到等待隊(duì)列中。
①『Node t = lastWaiter;』:獲取等待隊(duì)列的中的最后一個(gè)節(jié)點(diǎn),
②『t != null && t.waitStatus != Node.CONDITION』如果這個(gè)節(jié)點(diǎn)被取消了,那么調(diào)用『unlinkCancelledWaiters();』方法將等待隊(duì)列中被取消的節(jié)點(diǎn)移除。并重新獲取等待隊(duì)列中的最后一個(gè)節(jié)點(diǎn)(『t = lastWaiter』)
③ 為當(dāng)前線程創(chuàng)建一個(gè)waitStatus為“Node.CONDITION”的節(jié)點(diǎn)。
④ 將新創(chuàng)建好的節(jié)點(diǎn)加入到等待隊(duì)列的尾部:
a)如當(dāng)前等待隊(duì)列為空(即,上面獲取的t為null,也就是說(shuō),當(dāng)?shù)却?duì)列尾指針為null時(shí),則說(shuō)明此時(shí)等待隊(duì)列為空)那么需要先初始化firstWaiter,將其指向這個(gè)新創(chuàng)建的節(jié)點(diǎn)。然后將lastWaiter也指向這個(gè)新創(chuàng)建的節(jié)點(diǎn)。此時(shí)等待隊(duì)列中只有一個(gè)節(jié)點(diǎn),firstWaiter和lastWaiter都指向這個(gè)節(jié)點(diǎn)。
b)將等待隊(duì)列中最后一個(gè)節(jié)點(diǎn)的next屬性指向當(dāng)前這個(gè)新創(chuàng)建的節(jié)點(diǎn),然后將lastWaiter指向當(dāng)前這個(gè)新創(chuàng)建的節(jié)點(diǎn)。
⑤ 返回新創(chuàng)建的等待節(jié)點(diǎn)。
『AbstractQueuedSynchronizer#fullyRelease』
final int fullyRelease(Node node) {
boolean failed = true;
try {
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
使用當(dāng)前的狀態(tài)值執(zhí)行釋放;返回保存的狀態(tài)值。
若操作失敗,則取消節(jié)點(diǎn),并拋出異常。
①『int savedState = getState();』獲取當(dāng)前鎖的狀態(tài)值
② 使用獲取的狀態(tài)值執(zhí)行釋放操作『release(savedState)』,如果操作成功,則方法結(jié)束,返回釋放使用的保存的狀態(tài)值;如果操作失敗,則拋出“IllegalMonitorStateException”異常,并取消node節(jié)點(diǎn),即,將節(jié)點(diǎn)node的waitStatus設(shè)置為“Node.CANCELLED”。
『AbstractQueuedSynchronizer#isOnSyncQueue』
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
/*
* node.prev can be non-null, but not yet on queue because
* the CAS to place it on queue can fail. So we have to
* traverse from tail to make sure it actually made it. It
* will always be near the tail in calls to this method, and
* unless the CAS failed (which is unlikely), it will be
* there, so we hardly ever traverse much.
*/
return findNodeFromTail(node);
}
返回true,如果一個(gè)節(jié)點(diǎn)總是初始化于條件隊(duì)列中,并且當(dāng)前在同步隊(duì)列中等待獲取鎖。
① 如果node的waitStatus為“Node.CONDITION”或者node的prev為null,則說(shuō)明node節(jié)點(diǎn)當(dāng)前還沒(méi)有入隊(duì)同步隊(duì)列,方法結(jié)束,返回false;否則步驟[2]
② 接著判斷『if (node.next != null)』,如果為true,則說(shuō)明node已經(jīng)入隊(duì)完畢,則方法結(jié)束,返回true。否則步驟[3]
③ 調(diào)用『findNodeFromTail(node)』從同步隊(duì)列尾開(kāi)始尋找節(jié)點(diǎn)。此時(shí),node.prev非null,但是由于通過(guò)CAS將節(jié)點(diǎn)入隊(duì)的操作可能失敗導(dǎo)致當(dāng)前節(jié)點(diǎn)還未在同步隊(duì)列中(即,節(jié)點(diǎn)入隊(duì)操作還未完成)。所以我們需要從同步隊(duì)列尾部開(kāi)始向前遍歷以明確該節(jié)點(diǎn)是否在同步隊(duì)列中。在這種方法的調(diào)用中,節(jié)點(diǎn)總是靠近尾部,除非CAS失?。ú惶赡埽?,否則節(jié)點(diǎn)將在同步隊(duì)列尾部附近,所以我們幾乎不會(huì)經(jīng)歷很多遍歷。
『AbstractQueuedSynchronizer#findNodeFromTail』
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
從同步隊(duì)列尾向前查詢節(jié)點(diǎn),如果節(jié)點(diǎn)在同步隊(duì)列中,則返回true。
僅在『isOnSyncQueue』方法內(nèi)調(diào)用該方法。
從鎖的等待隊(duì)列尾部開(kāi)始向前遍歷,如果找到node節(jié)點(diǎn)則返回true;否則遍歷完整個(gè)等待隊(duì)列也就沒(méi)法找到node節(jié)點(diǎn),則返回false。
『AbstractQueuedSynchronizer#checkInterruptWhileWaiting』
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
用于檢測(cè)中斷,如果中斷在信號(hào)通知之前發(fā)生則返回“THROW_IE”,若中斷在信號(hào)通知之后發(fā)生則返回“REINTERRUPT”,或者如果沒(méi)有被中斷則返回“0”。
『AbstractQueuedSynchronizer#transferAfterCancelledWait』
final boolean transferAfterCancelledWait(Node node) {
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
/*
* If we lost out to a signal(), then we can't proceed
* until it finishes its enq(). Cancelling during an
* incomplete transfer is both rare and transient, so just
* spin.
*/
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
如果需要的話,在取消等待后,將節(jié)點(diǎn)轉(zhuǎn)移到同步隊(duì)列。如果線程在被信號(hào)通知之前取消等待了則返回true。
① 通過(guò)CAS的方式將節(jié)點(diǎn)的狀態(tài)從“Node.CONDITION”修改為“0”,如果成功,則說(shuō)明節(jié)點(diǎn)此時(shí)還沒(méi)有收到信號(hào)通知,此時(shí)將節(jié)點(diǎn)的waitStatus從“Node.CONDITION”修改為“0”就是在被信號(hào)通知前取消了節(jié)點(diǎn)對(duì)條件的等待,接著調(diào)用『enq(node)』將節(jié)點(diǎn)入隊(duì)到鎖的等待隊(duì)列中,并結(jié)束方法,返回true。
② CAS操作失敗,則說(shuō)明該等待條件的節(jié)點(diǎn)被其他線程信號(hào)通知了(一般是signalAll),那么自旋調(diào)用『isOnSyncQueue(node)』以確保節(jié)點(diǎn)入隊(duì)(鎖的等待隊(duì)列)完成后退出自旋(因?yàn)槿∠却龡l件期間一個(gè)未完成的轉(zhuǎn)換是罕見(jiàn)且瞬間的時(shí)期,所以使用自旋即可)。然后方法結(jié)束,返回false。
也就是說(shuō),首先該方法會(huì)確保node從條件等待隊(duì)列轉(zhuǎn)移到鎖的同步隊(duì)列中。node是因?yàn)樵摲椒ǖ膱?zhí)行而從條件等待隊(duì)列轉(zhuǎn)移到鎖的同步隊(duì)列的話,則返回true;否則如果node是因?yàn)閟ignal/signalAll信號(hào)通知而從條件等待隊(duì)列轉(zhuǎn)移到鎖的同步隊(duì)列的話,則返回false。
『AbstractQueuedSynchronizer#enq』
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;
}
}
}
}
使用自旋鎖的方式(自旋+CAS)插入節(jié)點(diǎn)到等待隊(duì)列,如果等待隊(duì)列為空則初始化隊(duì)列。
初始化隊(duì)列:創(chuàng)建一個(gè)空節(jié)點(diǎn)(即,new Node()),將head和tail都指向這個(gè)節(jié)點(diǎn)。
然后才是將我們待插入的節(jié)點(diǎn)插入,即:emptyNode -> newNode. head指向emptyNode,tail指向newNode。
『AbstractQueuedSynchronizer#unlinkCancelledWaiters』
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
while (t != null) {
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
從等待隊(duì)列中解除被取消等待節(jié)點(diǎn)的連接。該方法僅在持有鎖的時(shí)候調(diào)用。這個(gè)方法調(diào)用發(fā)生在取消發(fā)生在等待條件期間,并根據(jù)一個(gè)新的等待節(jié)點(diǎn)插入時(shí)lastWaiter看起來(lái)已經(jīng)被取消了。這個(gè)方法需要去避免垃圾的滯留在沒(méi)有信號(hào)通知的時(shí)候。所以即便它可能需要一個(gè)完全遍歷,這僅會(huì)在超時(shí)和取消發(fā)生在缺少通知的情況下發(fā)生。它會(huì)遍歷所有的節(jié)點(diǎn)而非停止在一個(gè)指定的目標(biāo),以便在取消風(fēng)暴期間不需要多次重新遍歷就可以將所有的垃圾節(jié)點(diǎn)解除鏈接。
該方法會(huì)從firstWaiter開(kāi)始遍歷整個(gè)等待隊(duì)列,將被取消(即,waitStatus != Node.CONDITION)的節(jié)點(diǎn)從等待隊(duì)列中移除。
①『Node t = firstWaiter;』:獲取等待隊(duì)列的頭結(jié)點(diǎn)。
② 從頭節(jié)點(diǎn)開(kāi)始遍歷等待隊(duì)列。
③『Node next = t.nextWaiter;』獲取當(dāng)前遍歷節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)。
④ 如果當(dāng)前節(jié)點(diǎn)被取消了(即,『t.waitStatus != Node.CONDITION』),那么將當(dāng)前節(jié)點(diǎn)的next字段置null(便于垃圾回收)。然后判斷『trail == null』,如果為true,則說(shuō)明目前是頭節(jié)點(diǎn)被取消了,那么設(shè)置『firstWaiter=next』,即當(dāng)前節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)。此時(shí),next節(jié)點(diǎn)可能是一個(gè)有效節(jié)點(diǎn),也可能是一個(gè)被取消的節(jié)點(diǎn)(如果是被取消的節(jié)點(diǎn),會(huì)在下一次循環(huán)的時(shí)候再次重新設(shè)置firstWaiter),也可能是一個(gè)null(如果為null,接下來(lái)就會(huì)退出循環(huán),說(shuō)明等待隊(duì)列為空了);如果『trail == null』為false,則說(shuō)明此遍歷到的被取消的節(jié)點(diǎn)不是頭節(jié)點(diǎn),并且trail指向了遍歷到目前為止等待隊(duì)列中最后一個(gè)有效的等待節(jié)點(diǎn),那么執(zhí)行『trail.nextWaiter = next;』以將當(dāng)前正在被遍歷的節(jié)點(diǎn)從等待隊(duì)列中解除連接。接著判斷『next == null』,若為true,則說(shuō)明當(dāng)前遍歷的被取消的節(jié)點(diǎn)是等待隊(duì)列的最后一個(gè)節(jié)點(diǎn),那么執(zhí)行『lastWaiter = trail;』將lastWaiter指向最后一個(gè)有效的等待節(jié)點(diǎn)。
⑤ 如果當(dāng)前節(jié)點(diǎn)沒(méi)有被取消(即,『t.waitStatus == Node.CONDITION』),那么將trail置為t,這說(shuō)明了trail指向了在遍歷等待隊(duì)列過(guò)程中的最后一個(gè)有效的等待節(jié)點(diǎn)。
⑥ 將t置為next,即當(dāng)前遍歷節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)。繼續(xù)步驟[3],直至整個(gè)等待隊(duì)列節(jié)點(diǎn)都遍歷完(即,next為null)。
signal/signalAll
- ** signal**
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
將當(dāng)前的條件等待隊(duì)列中將等待時(shí)間最長(zhǎng)的線程移動(dòng)到鎖的等待隊(duì)列中,如果存在這么一個(gè)線程的話。
① 判斷執(zhí)行該方法的當(dāng)前線程是否是持有排他鎖的線程,如果不是則拋出“IllegalMonitorStateException”異常。
② 當(dāng)執(zhí)行該方法的線程是持有排他鎖的線程時(shí),獲取條件等待隊(duì)列中的第一個(gè)等待節(jié)點(diǎn),若這個(gè)節(jié)點(diǎn)不為null,則執(zhí)行『doSignal(first)』來(lái)信號(hào)通知這個(gè)節(jié)點(diǎn)。
注意,因?yàn)闂l件等待節(jié)點(diǎn)是按照FIFO的順序操作節(jié)點(diǎn)的,也就是新的等待節(jié)點(diǎn)總是會(huì)添加對(duì)隊(duì)列尾部,所以隊(duì)列頭節(jié)點(diǎn)就是等待最長(zhǎng)時(shí)間的節(jié)點(diǎn)。
『AbstractQueuedSynchronizer#doSignal』
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
刪除并轉(zhuǎn)移節(jié)點(diǎn),直到命中一個(gè)未被取消的節(jié)點(diǎn)或者節(jié)點(diǎn)為null(節(jié)點(diǎn)為null,說(shuō)明等待隊(duì)列中已經(jīng)沒(méi)有一個(gè)有效的節(jié)點(diǎn)了,即等待隊(duì)列要么為空,要么等待隊(duì)列中的節(jié)點(diǎn)都是被取消的節(jié)點(diǎn))。
① 根據(jù)給定的first節(jié)點(diǎn),為起始遍歷直至獲取第一個(gè)有效等待節(jié)點(diǎn),并信號(hào)通知該節(jié)點(diǎn)。
② 將當(dāng)前節(jié)點(diǎn)的下一個(gè)等待節(jié)點(diǎn)(nextWaiter)設(shè)置為firstWaiter,然后判斷firstWaiter是否為null,如果為null則說(shuō)明當(dāng)前節(jié)點(diǎn)已經(jīng)是條件等待隊(duì)列中的最后一個(gè)節(jié)點(diǎn)了,那么將lastWaiter也置為null。
③ 將當(dāng)前遍歷節(jié)點(diǎn)的nextWaiter置為null(以便于當(dāng)前節(jié)點(diǎn)在方法結(jié)束后被垃圾收集器回收)
④ 執(zhí)行『transferForSignal』將節(jié)點(diǎn)從條件等待隊(duì)列轉(zhuǎn)移到同步隊(duì)列隊(duì)列中,如果操作成功,則當(dāng)前循環(huán)結(jié)束,方法返回;如果操作失敗,那么繼續(xù)從頭節(jié)點(diǎn)開(kāi)始循環(huán)步驟[2],直到成功轉(zhuǎn)移一個(gè)節(jié)點(diǎn)或者條件等待隊(duì)列為空為止。
- signalAll
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
將條件等待隊(duì)列中的所有線程轉(zhuǎn)移到鎖的等待隊(duì)列中。
① 判斷執(zhí)行該方法的當(dāng)前線程是否是持有排他鎖的線程,如果不是則拋出“IllegalMonitorStateException”異常。
② 當(dāng)執(zhí)行該方法的線程是持有排他鎖的線程時(shí),獲取條件等待隊(duì)列中的第一個(gè)等待節(jié)點(diǎn),若這個(gè)節(jié)點(diǎn)不為null,則執(zhí)行『doSignalAll(first)』來(lái)信號(hào)通知所有的節(jié)點(diǎn)。
『AbstractQueuedSynchronizer#doSignalAll』
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
刪除并轉(zhuǎn)移所有的節(jié)點(diǎn)。
① 將lastWaiter和firstWaiter置為null
② 從給定的節(jié)點(diǎn)為起始,開(kāi)始遍歷節(jié)點(diǎn),調(diào)用『transferForSignal(first);』來(lái)將節(jié)點(diǎn)從條件等待隊(duì)列中轉(zhuǎn)移到鎖的等待隊(duì)列中。
『isHeldExclusively』
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
判斷執(zhí)行該方法的當(dāng)前線程是否是持有排他鎖的線程。
如下情況,返回’true’:
a)執(zhí)行該方法的線程就是持有排他鎖的線程。
如下情況,返回’false’:
a)執(zhí)行該方法的線程不是持有排他鎖的線程。
b)當(dāng)前排他鎖沒(méi)有被任何線程所持有。
『AbstractQueuedSynchronizer#transferForSignal』
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
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).
*/
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
從條件等待隊(duì)列轉(zhuǎn)移一個(gè)節(jié)點(diǎn)到同步隊(duì)列中。如果成功返回true
返回:
a)true:成功從條件隊(duì)列中轉(zhuǎn)移一個(gè)節(jié)點(diǎn)到同步隊(duì)列中
b)false:在釋放信號(hào)通知之前,該節(jié)點(diǎn)被取消了。
① 通過(guò)CAS的方式將需要轉(zhuǎn)移的節(jié)點(diǎn)的狀態(tài)從“Node.CONDITION”修改為“0”。如果CAS操作失敗,則說(shuō)明這個(gè)節(jié)點(diǎn)的已經(jīng)被取消了。那么方法結(jié)束,返回false。
② 將修改完?duì)顟B(tài)后的節(jié)點(diǎn)加入到鎖等待隊(duì)列中(『enq(node)』),并得到加入到等待隊(duì)列后,當(dāng)前節(jié)點(diǎn)的前驅(qū)節(jié)點(diǎn)。
③ 若前驅(qū)節(jié)點(diǎn)的"waitStatus > 0”(即,waitStatus為“CANCELLED”)或者通過(guò)CAS的方式將前驅(qū)節(jié)點(diǎn)的waitStatus修改為“SIGNAL”失敗,則調(diào)用『LockSupport.unpark(node.thread);』將當(dāng)前線程喚醒(喚醒后的線程會(huì)繼續(xù)await中被掛起之后的流程)。
④ 否則,"waitStatus <= 0”并且通過(guò)CAS成功將前驅(qū)節(jié)點(diǎn)的waitStatus修改為了“SIGNAL”,以此來(lái)標(biāo)識(shí)當(dāng)前線程正在等待獲取鎖。
-
『signal』vs『signalAll』
signal解除的是條件等待隊(duì)列中第一個(gè)有效的節(jié)點(diǎn)(即,節(jié)點(diǎn)的waitStatus為“CONDITION”),這比解除所有線程的阻塞更加有效,但也存在危險(xiǎn)。如果signal的線程發(fā)現(xiàn)自己仍然不能運(yùn)行,那么它再次被阻塞(await)。如果沒(méi)有其他線程再次調(diào)用signal,那么系統(tǒng)就死鎖了。
signal/signalAll方法本質(zhì)上只是將條件等待隊(duì)列中的節(jié)點(diǎn)轉(zhuǎn)移到鎖的同步隊(duì)列中。因此,不能任務(wù)signal/signalAll方法調(diào)用后就會(huì)使得線程獲取鎖,線程什么時(shí)候獲取鎖,就是根據(jù)鎖的同步隊(duì)列FIFO的順序來(lái)決定的,只有同步隊(duì)列中的第一個(gè)線程才有權(quán)利去爭(zhēng)奪獲取鎖。
后記
如果文章有錯(cuò)不吝指教 :)