在理解J.U.C原理以及鎖機(jī)制之前,我們來(lái)介紹J.U.C框架最核心也是最復(fù)雜的一個(gè)基礎(chǔ)類:java.util.concurrent.locks.AbstractQueuedSynchronizer。 AQS AbstractQueuedSynchronizer,簡(jiǎn)稱AQS,是J.U.C最復(fù)雜的一個(gè)類,導(dǎo)致絕大多數(shù)講解并發(fā)原理或者實(shí)戰(zhàn)的時(shí)候都不會(huì)提到此類。但是虛心的作者愿意借助自己有限的能力和精力來(lái)探討一二(參考資源中也有一些作者做了部分的分析。)。 首先從理論知識(shí)開始,在了解了相關(guān)原理后會(huì)針對(duì)源碼進(jìn)行一些分析,最后加上一些實(shí)戰(zhàn)來(lái)描述。 上面的繼承體系中,AbstractQueuedSynchronizer是CountDownLatch/FutureTask/ReentrantLock/RenntrantReadWriteLock/Semaphore的基礎(chǔ),因此AbstractQueuedSynchronizer是Lock/Executor實(shí)現(xiàn)的前提。公平鎖、不公平鎖、Condition、CountDownLatch、Semaphore等放到后面的篇幅中說(shuō)明。 完整的設(shè)計(jì)原理可以參考Doug Lea的論文 The java.util.concurrent Synchronizer Framework ,這里做一些簡(jiǎn)要的分析。 基本的思想是表現(xiàn)為一個(gè)同步器,支持下面兩個(gè)操作: 獲取鎖:首先判斷當(dāng)前狀態(tài)是否允許獲取鎖,如果是就獲取鎖,否則就阻塞操作或者獲取失敗,也就是說(shuō)如果是獨(dú)占鎖就可能阻塞,如果是共享鎖就可能失敗。另外如果是阻塞線程,那么線程就需要進(jìn)入阻塞隊(duì)列。當(dāng)狀態(tài)位允許獲取鎖時(shí)就修改狀態(tài),并且如果進(jìn)了隊(duì)列就從隊(duì)列中移除。
釋放鎖:這個(gè)過(guò)程就是修改狀態(tài)位,如果有線程因?yàn)闋顟B(tài)位阻塞的話就喚醒隊(duì)列中的一個(gè)或者更多線程。
要支持上面兩個(gè)操作就必須有下面的條件:
目標(biāo)明確,要解決的問題也清晰了,那么剩下的就是解決上面三個(gè)問題。 狀態(tài)位的原子操作 這里使用一個(gè)32位的整數(shù)來(lái)描述狀態(tài)位,前面章節(jié)的原子操作的理論知識(shí)整好派上用場(chǎng),在這里依然使用CAS操作來(lái)解決這個(gè)問題。事實(shí)上這里還有一個(gè)64位版本的同步器(AbstractQueuedLongSynchronizer),這里暫且不談。 阻塞和喚醒線程 標(biāo)準(zhǔn)的JAVA API里面是無(wú)法掛起(阻塞)一個(gè)線程,然后在將來(lái)某個(gè)時(shí)刻再喚醒它的。JDK 1.0的API里面有Thread.suspend和Thread.resume,并且一直延續(xù)了下來(lái)。但是這些都是過(guò)時(shí)的API,而且也是不推薦的做法。 在JDK 5.0以后利用JNI在LockSupport類中實(shí)現(xiàn)了此特性。
上面的API中park()是在當(dāng)前線程中調(diào)用,導(dǎo)致線程阻塞,帶參數(shù)的Object是掛起的對(duì)象,這樣監(jiān)視的時(shí)候就能夠知道此線程是因?yàn)槭裁促Y源而阻塞的。由于park()立即返回,所以通常情況下需要在循環(huán)中去檢測(cè)競(jìng)爭(zhēng)資源來(lái)決定是否進(jìn)行下一次阻塞。park()返回的原因有三:
其實(shí)第三條就決定了需要循環(huán)檢測(cè)了,類似于通常寫的while(checkCondition()){Thread.sleep(time);}類似的功能。 有序隊(duì)列 在AQS中采用CHL列表來(lái)解決有序的隊(duì)列的問題。
對(duì)于入隊(duì)列(enqueue):采用CAS操作,每次比較尾結(jié)點(diǎn)是否一致,然后插入的到尾結(jié)點(diǎn)中。
對(duì)于出隊(duì)列(dequeue):由于每一個(gè)節(jié)點(diǎn)也緩存了一個(gè)狀態(tài),決定是否出隊(duì)列,因此當(dāng)不滿足條件時(shí)就需要自旋等待,一旦滿足條件就將頭結(jié)點(diǎn)設(shè)置為下一個(gè)節(jié)點(diǎn)。
實(shí)際上這里自旋等待也是使用LockSupport.park()來(lái)實(shí)現(xiàn)的。 AQS里面有三個(gè)核心字段:
其中state描述的有多少個(gè)線程取得了鎖,對(duì)于互斥鎖來(lái)說(shuō)state<=1。head/tail加上CAS操作就構(gòu)成了一個(gè)CHL的FIFO隊(duì)列。下面是Node節(jié)點(diǎn)的屬性。
這一個(gè)小節(jié)主要介紹了一些理論背景和相關(guān)的數(shù)據(jù)結(jié)構(gòu),在下一個(gè)小節(jié)中將根據(jù)以上知識(shí)來(lái)了解Lock.lock/unlock是如何實(shí)現(xiàn)的。
參考資料: (1)ReentrantLock代碼剖析之ReentrantLock.lock ReentrantLock代碼剖析之ReentrantLock.unlock ReentrantLock代碼剖析之ReentrantLock.lockInterruptibly (2)java多線程--java.util.concurrent.locks.AbstractQueuedSynchronizer解析(只包含多線程同步示例) (4)AbstractQueuedSynchronizer源碼解析之ReentrantLock(一) AbstractQueuedSynchronizer源碼解析之ReentrantLock(二) (5)The java.util.concurrent Synchronizer Framework
|
|
來(lái)自: dtl樂學(xué)館 > 《java高級(jí)》