日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

深入淺出 Java Concurrency (7): 鎖機(jī)制 part 2 AQS

 dtl樂學(xué)館 2015-06-10

在理解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)描述。

image

上面的繼承體系中,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ì)列中移除。

while(synchronization state does not allow acquire){

    enqueue current thread if not already queued;

    possibly block current thread;

}

dequeue current thread if it was queued;

釋放鎖:這個(gè)過(guò)程就是修改狀態(tài)位,如果有線程因?yàn)闋顟B(tài)位阻塞的話就喚醒隊(duì)列中的一個(gè)或者更多線程。

update synchronization state;

if(state may permit a blocked thread to acquire)

    unlock one or more queued threads;

要支持上面兩個(gè)操作就必須有下面的條件:

  • 原子性操作同步器的狀態(tài)位
  • 阻塞和喚醒線程
  • 一個(gè)有序的隊(duì)列

目標(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)了此特性。

LockSupport.park()
LockSupport.park(Object)
LockSupport.parkNanos(Object, long)
LockSupport.parkNanos(long)
LockSupport.parkUntil(Object, long)
LockSupport.parkUntil(long)
LockSupport.unpark(Thread)

上面的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()返回的原因有三:

  • 其他某個(gè)線程調(diào)用將當(dāng)前線程作為目標(biāo)調(diào)用 unpark;
  • 其他某個(gè)線程中斷當(dāng)前線程;
  • 該調(diào)用不合邏輯地(即毫無(wú)理由地)返回。

其實(shí)第三條就決定了需要循環(huán)檢測(cè)了,類似于通常寫的while(checkCondition()){Thread.sleep(time);}類似的功能。

有序隊(duì)列

在AQS中采用CHL列表來(lái)解決有序的隊(duì)列的問題。

image AQS采用的CHL模型采用下面的算法完成FIFO的入隊(duì)列和出隊(duì)列過(guò)程。

對(duì)于入隊(duì)列(enqueue):采用CAS操作,每次比較尾結(jié)點(diǎn)是否一致,然后插入的到尾結(jié)點(diǎn)中。

do {

        pred = tail;

}while ( !compareAndSet(pred,tail,node) );

對(duì)于出隊(duì)列(dequeue):由于每一個(gè)節(jié)點(diǎn)也緩存了一個(gè)狀態(tài),決定是否出隊(duì)列,因此當(dāng)不滿足條件時(shí)就需要自旋等待,一旦滿足條件就將頭結(jié)點(diǎn)設(shè)置為下一個(gè)節(jié)點(diǎn)。

while (pred.status != RELEASED) ;

head  = node;

實(shí)際上這里自旋等待也是使用LockSupport.park()來(lái)實(shí)現(xiàn)的。

AQS里面有三個(gè)核心字段:

private volatile int state;

private transient volatile Node head;

private transient volatile Node tail;

其中state描述的有多少個(gè)線程取得了鎖,對(duì)于互斥鎖來(lái)說(shuō)state<=1。head/tail加上CAS操作就構(gòu)成了一個(gè)CHL的FIFO隊(duì)列。下面是Node節(jié)點(diǎn)的屬性。

volatile int waitStatus; 節(jié)點(diǎn)的等待狀態(tài),一個(gè)節(jié)點(diǎn)可能位于以下幾種狀態(tài):

  • CANCELLED = 1: 節(jié)點(diǎn)操作因?yàn)槌瑫r(shí)或者對(duì)應(yīng)的線程被interrupt。節(jié)點(diǎn)不應(yīng)該留在此狀態(tài),一旦達(dá)到此狀態(tài)將從CHL隊(duì)列中踢出。
  • SIGNAL = -1: 節(jié)點(diǎn)的繼任節(jié)點(diǎn)是(或者將要成為)BLOCKED狀態(tài)(例如通過(guò)LockSupport.park()操作),因此一個(gè)節(jié)點(diǎn)一旦被釋放(解鎖)或者取消就需要喚醒(LockSupport.unpack())它的繼任節(jié)點(diǎn)。
  • CONDITION = -2:表明節(jié)點(diǎn)對(duì)應(yīng)的線程因?yàn)椴粷M足一個(gè)條件(Condition)而被阻塞。
  • 0: 正常狀態(tài),新生的非CONDITION節(jié)點(diǎn)都是此狀態(tài)。
  • 非負(fù)值標(biāo)識(shí)節(jié)點(diǎn)不需要被通知(喚醒)。

volatile Node prev;此節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)。節(jié)點(diǎn)的waitStatus依賴于前一個(gè)節(jié)點(diǎn)的狀態(tài)。

volatile Node next;此節(jié)點(diǎn)的后一個(gè)節(jié)點(diǎn)。后一個(gè)節(jié)點(diǎn)是否被喚醒(uppark())依賴于當(dāng)前節(jié)點(diǎn)是否被釋放。

volatile Thread thread;節(jié)點(diǎn)綁定的線程。

Node nextWaiter;下一個(gè)等待條件(Condition)的節(jié)點(diǎn),由于Condition是獨(dú)占模式,因此這里有一個(gè)簡(jiǎn)單的隊(duì)列來(lái)描述Condition上的線程節(jié)點(diǎn)。

 

AQS 在J.U.C里面是一個(gè)非常核心的工具,而且也非常復(fù)雜,里面考慮到了非常多的邏輯實(shí)現(xiàn),所以在后面的章節(jié)中總是不斷的嘗試介紹AQS的特性和實(shí)現(xià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解析(只包含多線程同步示例)

(3)處理 InterruptedException

(4)AbstractQueuedSynchronizer源碼解析之ReentrantLock(一)  AbstractQueuedSynchronizer源碼解析之ReentrantLock(二)

(5)The java.util.concurrent Synchronizer Framework

 

 



2009-2014 IMXYLZ |求賢若渴

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多