Java同步框架AQS原文分析

0粘招、引言

自J2SE1.5開始,java中的同步類(Lock嘉冒,Semphore等等)都基于AbstractQueuedSynchronizer(后文簡稱AQS)才睹。AQS提供了一種原子式管理同步狀態(tài)、阻塞和喚醒線程功能以及隊列模型的簡單框架淳梦。

本文主要是分析此框架的實現(xiàn)者Doug Lea寫的一篇介紹AQS的論文(→猛戳這里拿原文←)析砸,并沒有完全翻譯原文,所以想看原文的在上面拿原文爆袍。

1首繁、基本功能

同步器至少要有以下兩種類型的方法acquire和release

  • acquire:至少要有一個操作能實現(xiàn)對調用線程的阻塞,直到同步器允許它進行操作陨囊。
  • release:至少要有一個操作能用一種方式解鎖一個或者更多個已經(jīng)阻塞的線程改變同步狀態(tài)弦疮。

同時,同步器還需要支持以下幾種功能:

  • 非阻塞式的同步過程嘗試(tryLock)
  • 可選的超時機制蜘醋,可以允許程序放棄等待
  • 可以通過中斷執(zhí)行取消

而為了適應不同的同步器胁塞,同步器要支持兩種模式

  • 獨占式exclusive。要保證一次只有一個線程可以經(jīng)過阻塞點
  • 共享式shared压语∠邢龋可以允許多個線程阻塞點

2、性能要求

AQS對性能改進的關注點不是主要在于減小空間的開銷和時間的開銷无蜂。
原因是:對于開發(fā)者來說,只在需要的時候構建同步器蒙谓,實在沒有必要為了這部分空間的消耗去壓縮空間斥季。與此同時,同步器大部分情況是用在多線程的情況下,產(chǎn)生一些競爭也是可以想象到的酣倾。

AQS的性能重點在于可擴展性

AQS的幾個改進目標

  1. 可預見性的保證同步器的效率舵揭,甚至在其發(fā)生競爭的情形下。
  2. 減少那些已經(jīng)被允許通過阻塞點的線程但是沒有通過的消耗時間躁锡。

對比自旋鎖來說午绳,自旋鎖的響應速度很快,但是在線程競爭特別激烈的情況下映之,由于大量的內存讀取拦焚,會降低其響應的速度。

AQS的框架必須能夠提供一些監(jiān)視和檢查的基本操作杠输,以便用戶發(fā)現(xiàn)和緩解瓶頸赎败。例如:提供一個方式,決定多少個線程會被阻塞蠢甲。

3僵刮、設計與實現(xiàn)

acquire和release操作的偽代碼可以很容易的寫出來如下:

acquire{
    while (同步狀態(tài)不允許獲取) {
        若當前線程沒有入隊,那么就將其入隊鹦牛;
        可能會阻塞當前線程搞糕;
    }
    如果它已經(jīng)入隊了,就將其出隊
}

release {
    更新同步狀態(tài)
    if(狀態(tài)表示允許一個阻塞的線程去acquire)
       釋放一個或多個隊列中的線程
}

3.1曼追、同步狀態(tài)State

AQS采用了int(4個字節(jié))的變量來持有同步狀態(tài)窍仰。使用getStatesetState拉鹃, compareAndSetState方法來進行狀態(tài)的獲取和更新辈赋。

以上的方法,它們依賴于volatile這個機制來進行讀寫膏燕,compare-and-swap機制去實現(xiàn)compareAndSetState方法钥屈。(CAS操作如果不清楚的可以自行搜索相關的內容)

AQS是一個抽象類,它的tryAcquire和tryRelease方法都需要子類去實現(xiàn)坝辫。兩個方法都支持傳入一個int類型的參數(shù)篷就。這個參數(shù)主要用來實現(xiàn)不同子類功能的〗Γ【例如】:reentrant lock竭业,當在返回一個條件等待后重新去獲取鎖權限是,它會重新建立一個遞歸計數(shù)及舍。

3.2未辆、阻塞

AQS沒有采用Thread.suspendThread.resume這兩種方式,以上兩種方式都有嚴重的安全問題锯玛,容易造成死鎖等咐柜。

AQS采用了java.util.concurrent.locks包下的LockSupport類兼蜈。該類可以響應中斷操作,可以設置超時時間等拙友。此機制與Win32內的“消費事件”機制为狸,Linux NPTL線程庫的方式類似。

3.3遗契、核心隊列

AQS框架的核心是阻塞線程的隊列辐棒。也就是一個FIFO(先進先出)隊列。AQS不支持基于優(yōu)先級的同步器牍蜂。

AQS的鎖策略采用的CLH而不是MCS漾根,原因是CLH要比MCS更適合處理取消和超時。

CLH隊列的入隊和出隊操作是與它的鎖操作息息相關捷兰。它有兩個原子操作更新域立叛,head和taiil。初始化時贡茅,將指向一個虛假的節(jié)點秘蛇。


隊列格式.png

每個節(jié)點的release狀態(tài)都保存在它的前驅節(jié)點內,while (pred.status != RELEASED);后就可以開始自旋顶考。若持有前驅節(jié)點的域赁还,CLH鎖可以處理超時和其他形式的取消操作。

AQS對CLH機制有兩點修改

3.3.1驹沿、修改點1:增加后繼節(jié)點訪問域

AQS增加了節(jié)點node訪問其后繼節(jié)點的next域艘策。由于AQS隊列是雙向隊列,所以CAS操作也沒有很好的方式對兩個方向都做到完全的原子性更新渊季。后繼結點的更新就采用了
pred.next = node; 這種方式朋蔫。
當后繼結點可能出現(xiàn)退出的情形,AQS會通過pred域往前遍歷確定是否是真的退出了却汉。

3.3.2驯妄、修改點2:采用狀態(tài)域控制阻塞

CLH采用自旋進行線程的阻塞,AQS沒有采用這種方式合砂,而采用之前介紹過的State字段進行阻塞青扔。

AQS需要控制在頭節(jié)點調用tryAcquire方法適合才允許通過,其他情況acquire和block都會失敗翩伪。每次只需檢查當前節(jié)點的前驅節(jié)點是不是head微猖,這一點減少了CLH對內存的讀取競爭,同時還能避免不必要的阻塞和喚醒操作缘屹。

【原因】:在調用park方法前凛剥,線程會設置一個“SIGNAL”信號,然后重新檢查同步狀態(tài)轻姿,再確定是否需要再次調用park方法犁珠。

3.3.3傅瞻、修改點3:利用垃圾回收機制進行節(jié)點存儲管理

AQS主要使用在出隊的時候置null方式回收節(jié)點內存,這可以有效的避免復雜的處理和瓶頸盲憎。


這里我們可以給出更加具體的acquire方法

acquire {
    if (!tryAcquire(arg)) {
        node = 創(chuàng)建隊列并且新入隊節(jié)點;
        pred = 節(jié)點的有效前驅節(jié)點;
        while (pred 不是頭節(jié)點 || !tryAcquire(arg)) {
            if (pred的狀態(tài)位是Signal信號)
                park();
            else
                CAS操作設置pred的Signal信號;
            pred = node節(jié)點的有效前驅節(jié)點;
        }
        head = node;
    }
}

而release方法也可以得到如下:

release {
    if (tryRelease(arg) && 頭節(jié)點的狀態(tài)是Signal) {
        將頭節(jié)點的狀態(tài)設置為不是Signal;
        如果頭節(jié)點的后繼結點存在,則將其喚醒胳挎。
    }
}

【時間復雜度】acquire的循環(huán)次數(shù)由tryAcquire方法的性質決定饼疙。在不考慮線程等消耗,以及取消操作的情況下慕爬,它的時間復雜度是O(1)窑眯。在取消操作的情況下,確認前驅和后繼結點后重置同步狀態(tài)需要O(n)次遍歷(n為隊列的長度)医窿。

3.4磅甩、條件隊列

AQS中的ConditionObject提供一個能讓同步器使用的類。它既符合Lock接口姥卢,又能持有互斥的同步機制卷要。

ConditionObject類提供了類似awaitsignal独榴,signalAll操作的API僧叉。這些方法的作用與Object.wait方法是一樣的。ConditionObject使用與同步器同樣的內部隊列棺榔,不過與同步器存儲在分開的條件隊列中瓶堕。

3.4.1、基本操作流程

基本的await操作如下

  1. 創(chuàng)建并添加新的節(jié)點到條件隊列中症歇;
  2. 釋放鎖郎笆;
  3. 阻塞直到節(jié)點在鎖隊列中
  4. 重新獲取鎖。

基本的signal操作如下

  1. 傳遞條件隊列的第一個節(jié)點到鎖隊列中

3.4.2忘晤、取消流程

上述基本操作的實現(xiàn)并不困難宛蚓,而處理取消,超時和線程的中斷是實現(xiàn)的難點德频。

  • 中斷發(fā)生在await操作之前苍息,此方法一定要拋出一個InterruptedException
  • 中斷發(fā)生在await操作之后,此方法不拋出異常壹置,而是系統(tǒng)的中斷狀態(tài)集

條件隊列需要一個狀態(tài)位竞思,當出現(xiàn)Signal信號失敗,就將信號傳遞到隊列的下一個節(jié)點內钞护。而如果出現(xiàn)Cancel信號失敗盖喷,就取消傳遞操作,喚醒鎖的重新獲取操作难咕。

4课梳、用法

4.1距辆、控制公平性

AQS并不保證同步器一定是公平的。tryAcquire方法是在入隊操作前的一個檢驗暮刃,因此完全可以在入隊前跨算,“偷取”獲取的權限。

  • 非公平的FIFO策略(獲取到鎖的順序不一定是隊列中的順序)椭懊,將tryAcquire方法中每次進入都會進行競爭诸蚕,無論當前線程是否是隊列的頭節(jié)點。只要進入的線程速度更快氧猬,那么隊列中的節(jié)點即使解除了阻塞背犯,依然會重新阻塞回去。

  • 公平的FIFO策略盅抚,只需要將tryAcquire方法在當前線程不是隊列的頭節(jié)點時放回失敗就行漠魏。次之的方式,只需要判斷隊列是否為空妄均,空隊列就可以放回tryAcquire成功柱锹。

同步器的公平性設置主要是在多處理器情況下,才能發(fā)揮出其水平丛晦。多處理器往往會有更多的競爭奕纫,也就更有可能發(fā)生一個線程發(fā)現(xiàn)鎖現(xiàn)在被其他線程需要的情形。

4.2烫沙、同步器

  • ReentrantLock
  • ReentrantReadWriteLock
  • Semaphore
  • CountDownLatch
  • FutureTask(1.5以后不再使用AQS)
  • SynchronousQueue

自我總結

這里開始就不是論文中的內容了匹层。AQS的基本理解就在上述的文章中,后面我們再深入到AQS的源碼中看它具體的實現(xiàn)方式锌蓄。

AQS的核心內容就在于它處理阻塞和非阻塞的方式升筏,如何用隊列實現(xiàn)不同功能的同步器,如何控制同步器的性能瘸爽。

如果你對上述問題還不了解您访,可以再深入看看原文。

參考文章

  1. Doug Lea. The java.util.concurrent Synchronizer Framework.

如有翻譯不周到的地方剪决,歡迎批評指正灵汪!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市柑潦,隨后出現(xiàn)的幾起案子享言,更是在濱河造成了極大的恐慌,老刑警劉巖渗鬼,帶你破解...
    沈念sama閱讀 219,270評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件览露,死亡現(xiàn)場離奇詭異,居然都是意外死亡譬胎,警方通過查閱死者的電腦和手機差牛,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評論 3 395
  • 文/潘曉璐 我一進店門命锄,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人偏化,你說我怎么就攤上這事脐恩。” “怎么了侦讨?”我有些...
    開封第一講書人閱讀 165,630評論 0 356
  • 文/不壞的土叔 我叫張陵被盈,是天一觀的道長扶认。 經(jīng)常有香客問我厌殉,道長弄唧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,906評論 1 295
  • 正文 為了忘掉前任怜俐,我火速辦了婚禮,結果婚禮上邓尤,老公的妹妹穿的比我還像新娘拍鲤。我一直安慰自己,他們只是感情好汞扎,可當我...
    茶點故事閱讀 67,928評論 6 392
  • 文/花漫 我一把揭開白布季稳。 她就那樣靜靜地躺著,像睡著了一般澈魄。 火紅的嫁衣襯著肌膚如雪景鼠。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評論 1 305
  • 那天痹扇,我揣著相機與錄音铛漓,去河邊找鬼。 笑死鲫构,一個胖子當著我的面吹牛浓恶,可吹牛的內容都是我干的。 我是一名探鬼主播结笨,決...
    沈念sama閱讀 40,442評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼包晰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了炕吸?” 一聲冷哼從身側響起伐憾,我...
    開封第一講書人閱讀 39,345評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎算途,沒想到半個月后塞耕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡嘴瓤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,984評論 3 337
  • 正文 我和宋清朗相戀三年扫外,在試婚紗的時候發(fā)現(xiàn)自己被綠了莉钙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,117評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡筛谚,死狀恐怖磁玉,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情驾讲,我是刑警寧澤蚊伞,帶...
    沈念sama閱讀 35,810評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站吮铭,受9級特大地震影響时迫,放射性物質發(fā)生泄漏。R本人自食惡果不足惜谓晌,卻給世界環(huán)境...
    茶點故事閱讀 41,462評論 3 331
  • 文/蒙蒙 一掠拳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纸肉,春花似錦溺欧、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至烦味,卻和暖如春聂使,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背谬俄。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評論 1 272
  • 我被黑心中介騙來泰國打工岩遗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凤瘦。 一個月前我還...
    沈念sama閱讀 48,377評論 3 373
  • 正文 我出身青樓宿礁,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蔬芥。 傳聞我的和親對象是個殘疾皇子梆靖,可洞房花燭夜當晚...
    茶點故事閱讀 45,060評論 2 355

推薦閱讀更多精彩內容