讓人高看一眼的并發(fā)工具類Phaser

前面介紹的幾個工具類大家或多或少都有聽說過,但是Phaser卻很少人知道,如果在面試中問到你能回答上來,會讓面試官高看一眼的。?

使用場景

現(xiàn)實中很多事情都是分很多個階段,并且每個階段都要都完成才能進行下一個階段。比如就像大學(xué)時去吃飯一樣,寢室?guī)讉€都是在11點左右準備去吃午飯,首先是大家都要起床,然后一起出門,一起去吃完飯,最后一起回家。每一個過程中都有完成的先后,但是都會等待一起完成才會做接下來的事情。

每個階段的等待好像都可以用上一篇文章講的CyclicBarrier來實現(xiàn),但是在這個場景下,由于有多個階段,所以用CyclicBarrier難免有點復(fù)雜了。就可以利用Phaser來實現(xiàn)。

用Phaser來實現(xiàn)

直接上代碼吧,代碼比較簡單,沒有設(shè)置額外的信息,具體代碼如下圖:


首先“phaser.bulkRegister(4);”與CyclicBarrier設(shè)置調(diào)用最大await方法次數(shù)一樣,這里的意思是當phaser的arrive系列方法執(zhí)行4次后當前階段完成。

Person實現(xiàn)Runnable,有4個方法getUp()、goOut()、eat()、goHome()來表示做一個事情的4個階段,每個方法在執(zhí)行的最后調(diào)用“phaser.arriveAndAwaitAdvance();”來等待其他人完成,run()方法按順序執(zhí)行這4個方法。

new出4個Person并開始執(zhí)行方法,4個對象所有前一個方法執(zhí)行完成后才會開始執(zhí)行下一個方法。執(zhí)行結(jié)果如下:

id=2起床

id=1起床

id=0起床

id=3起床

id=3出門

id=0出門

id=2出門

id=1出門

id=1吃飯

id=2吃飯

id=0吃飯

id=3吃飯

id=3回家

id=2回家

id=0回家

id=1回家

可以看到幾個線程相同的方法執(zhí)行先后順序不一樣,但是肯定是所有同樣的方法執(zhí)行完成后才開始執(zhí)行下一個方法。

結(jié)構(gòu)分析

Phaser有三個關(guān)鍵屬性,首先是long類型的屬性state,高位的32位用來保存Phaser進行在第幾個階段,然后接下來的16位用來保存當前階段最大參與線程數(shù)量,最低16用來保存當前階段已經(jīng)到達的線程數(shù)量,當為0是會喚醒阻塞線程。

evenQ和oddQ屬性是鏈表結(jié)構(gòu),存儲已完成處于等待的隊列,與AQS中的Node類似,包含有一個Thead屬性,用來保存線程用的,當最后一個參與者完成任務(wù)后喚醒隊列中的線程繼續(xù)執(zhí)行下一個階段的任務(wù),或者結(jié)束任務(wù)。這里用兩個鏈表是為了在單雙階段交替使用。

分析arriveAndAwaitAdvance方法

Phaser提供了很多方法,這里分析最重要的一個方法,理解原理其他方法應(yīng)該也差不多了。源碼主要的流程圖如下圖:


主要流程是:

(1)從state中計算出當前的階段phaser和未到達的線程數(shù)量unarrived;

(2)修改state的值減一,因為unarrived是保存在state的最后16位,所以state減一就表示unarrived減一;

(3)unarrived大于1則調(diào)用internalAwaitAdvance阻塞線程;

(4)否則說明unarrived等于1,也就是當前線程是最后一個達到的,所以調(diào)用onAdvance喚醒阻塞線程;

(5)最后兩步就是修改state的值,實際上就是為下一個階段做準備,最后再次喚醒等待線程,實際上onAdvance也做了這些事情,這里是多次保證數(shù)據(jù)完整。

總結(jié)

可以看到Phaser與AQS下的工具類也差不多,都是維護一個state和同步隊列,不過Phaser的state是long類型,前32為用來表達第幾個階段,緊接著16為用來表達最大線程數(shù),最后16為用來表達未到達的線程數(shù),也就是還可以有多少個線程調(diào)用這個方法就會是否所有阻塞的線程,并進入下一個階段。

把state分成三個部分,在每個階段只用-1就行,當最后16為減到0時,就進入下一個階段,也就是把高位32為+1,低位16設(shè)置成中間的16位,實現(xiàn)重置階段并記錄進行的階段。

Phaser相比CyclicBarrier或者CountDownLatch的優(yōu)勢是可以實現(xiàn)更多階段的控制;同時Phaser每個階段的任務(wù)數(shù)量可以控制(這篇文章這個不是重點,有興趣可以去了解),而一個CyclicBarrier或者CountDownLatch的數(shù)量一旦確定不可修改。


Java程序員日常學(xué)習筆記,如理解有誤歡迎各位交流討論!


?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容