首页
/
每日頭條
/
圖文
/
ase緩存機制
ase緩存機制
更新时间:2025-04-22 21:54:25
Phaser并發階段器

Phaser由JDK1.7提出,是一個複雜強大的同步輔助類,是對同步工具類CountDownLatch和CyclicBarrier的綜合升級,能夠支持分階段實現等待的業務場景。

我們可以回憶下CountDownLatch講的是先指定N個線程,在N個線程幹完活之前,其它線程都需要等待(導遊等待旅遊團所有人上車才能開車),而CyclicBarrier講的是先指定N個線程。等N個線程到齊了大家同時幹活(多個驢友相約去旅遊,先到的需要等待後來的),而Phaser是兩者的結合,可以理解為先指定N個線程,等N個線程到齊後開始幹第一階段的活,等第一階段所有的線程都幹完活了,接着N個線程開始幹第二階段的活,直到所有的階段完成工作,程序結束,當然需要注意的是每個階段可以根據業務需要新增或者删除一些線程,并不是開始指定多少個線程每個階段就必須有多少個線程。

入門體驗

看了概念可能不容易理解,從一個小demo入手體驗下

publicclassPhaserDemo1{ //指定随機種子 privatestaticRandomrandom=newRandom(System.currentTimeMillis()); publicstaticvoidmain(String[]args){ Phaserphaser=newPhaser(); //将線程注冊到phaser phaser.register(); for(inti=0;i<5;i ){ Tasktask=newTask(phaser); task.start(); } phaser.arriveAndAwaitAdvance(); System.out.println("alltaskexecuteclose"); } staticclassTaskextendsThread{ Phaserphaser; publicTask(Phaserphaser){ this.phaser=phaser; this.phaser.register(); } @Override publicvoidrun(){ try{ System.out.println(Thread.currentThread().getName() "開始執行"); TimeUnit.SECONDS.sleep(random.nextInt(5)); System.out.println(Thread.currentThread().getName() "執行完畢"); //類似CountDownLatch中的await phaser.arriveAndAwaitAdvance(); }catch(InterruptedExceptione){ e.printStackTrace(); } } } }

不知道有沒有這樣的疑惑,phaser.register是向phaser去注冊這個線程,那麼為什麼主線程也需要注冊呢?

其實很簡單主線程需要等待所有子線程執行完畢才能繼續往下面執行所以必須要phaser.arriveAndAwaitAdvance();阻塞等待,而這個語句是意思當前線程已經到達屏障,在此等待一段時間等條件滿足後需要向下一個屏障繼續執行,如果沒有主線程的phaser.register,直接調用phaser.arriveAndAwaitAdvance,在源碼中提到可能會有異常,所以必須在主程序中注冊phaser.register();

/*<p>Itisausageerrorforanunregisteredpartytoinvokethis *method.However,thiserrormayresultinan{@code *IllegalStateException}onlyuponsomesubsequentoperationon *thisphaser,ifever. */ 譯: 未注冊方調用此函數是一個使用錯誤方法。但是,這個錯誤可能會導緻 {@codeIllegalStateException}僅在一些後續操作這個相位器,如果有的話。

Phaser解決分科考試問題

從體驗的示例中其實沒看出其優勢在哪裡,上訴場景完全可以采用CountDownLatch,所以現在換一種場景來說明Phaser的優勢。

假設某校舉行期末考試,有三門考試語文、數學、英語,每門課允許學生提前交卷,隻有當所有學生完成考試後才能舉行下一次的考試,這就是典型的分階段任務處理,示例圖如下。

ase緩存機制(Phaser并發階段器)1

将上訴場景語義化如下

publicclassPhaserExam{ publicstaticRandomrandom=newRandom(System.currentTimeMillis()); publicstaticvoidmain(String[]args){ //一次初始化2個相當于兩次register Phaserphaser=newPhaser(2); for(inti=0;i<2;i ){ Examexam=newExam(phaser,random.nextLong()); exam.start(); } } staticclassExamextendsThread{ Phaserphaser; Longid; publicExam(Phaserphaser,Longid){ this.phaser=phaser; this.id=id; } @Override publicvoidrun(){ try{ System.out.println(Thread.currentThread().getName() "===開始語文考試"); TimeUnit.SECONDS.sleep(random.nextInt(5)); System.out.println(Thread.currentThread().getName() "===結束語文考試"); phaser.arriveAndAwaitAdvance(); System.out.println(Thread.currentThread().getName() "===開始數學考試"); TimeUnit.SECONDS.sleep(random.nextInt(5)); System.out.println(Thread.currentThread().getName() "===結束數學考試"); phaser.arriveAndAwaitAdvance(); System.out.println(Thread.currentThread().getName() "===開始英語考試"); TimeUnit.SECONDS.sleep(random.nextInt(5)); System.out.println(Thread.currentThread().getName() "===結束英語考試"); phaser.arriveAndAwaitAdvance(); }catch(InterruptedExceptione){ e.printStackTrace(); } } } }

代碼執行結果如下,可以看到三個階段都是等待所有線程執行完畢後才往下執行,相當于多個栅欄。

ase緩存機制(Phaser并發階段器)2

到這裡請注意,通過Phaser類的構造方法構建的party數,也就是線程數需要和循環的次數對應,不然可能影響後續階段器的正常運行。

兩個重要狀态

在Phaser内有2個重要狀态,分别是phase和party,乍一看很難理解,他們的定義如下。

phase就是階段,如上面提到的語文、數學、英語考試這每個考試對應一個階段,不過phase是從0開始的,當所有任務執行完畢,準備進入下一個階段時phase就會加一。

party對應注冊到Phaser線程數,party初始值有兩種形式

  • 方法一就是通過Phaser的有參構造初始化party值。
  • 方法二采用動态注冊方法phaser.register()或phaser.bulkRegister(線程數)指定線程數,注銷線程調用phaser.arriveAndDeregister()方法party值會減一。
Phaser常用API

Phaser常用API總結如下所示

//獲取Phaser階段數,默認0 publicfinalintgetPhase(); //向Phaser注冊一個線程 publicintregister(); //向Phaser注冊多個線程 publicintbulkRegister(intparties); //獲取已經注冊的線程數,也就是重要狀态party的值 publicintgetRegisteredParties(); //到達并且等待其它線程到達 publicintarriveAndAwaitAdvance(); //到達後注銷不等待其它線程,繼續往下執行 publicintarriveAndDeregister(); //已到達線程數 publicintgetArrivedParties(); //未到達線程數 publicintgetUnarrivedParties(); //Phaser是否結束隻有當party的數量是0或者調用方法forceTermination時才會結束 publicbooleanisTerminated(); //結束Phaser publicvoidforceTermination();

代碼演示如下

publicclassPhaserApiTest{ publicstaticvoidmain(String[]args)throwsInterruptedException{ Phaserphaser=newPhaser(5); System.out.println("當前階段" phaser.getPhase()); System.out.println("注冊線程數===" phaser.getRegisteredParties()); //向phaser注冊一個線程 phaser.register(); System.out.println("注冊線程數===" phaser.getRegisteredParties()); //向phaser注冊多個線程,批量注冊 phaser.bulkRegister(4); System.out.println("注冊線程數===" phaser.getRegisteredParties()); newThread(()->{ //到達且等待 phaser.arriveAndAwaitAdvance(); System.out.println(Thread.currentThread().getName() "===執行1"); }).start(); newThread(()->{ //到達不等待,從phaser中注銷一個線程 phaser.arriveAndDeregister(); System.out.println(Thread.currentThread().getName() "===執行2"); }).start(); TimeUnit.SECONDS.sleep(3); System.out.println("已到達線程數===" phaser.getArrivedParties()); System.out.println("未到達線程數===" phaser.getUnarrivedParties()); System.out.println("Phaser是否結束" phaser.isTerminated()); phaser.forceTermination(); System.out.println("Phaser是否結束" phaser.isTerminated()); } }

執行結果如下所示

ase緩存機制(Phaser并發階段器)3

arriveAndAwaitAdvance解析

arriveAndAwaitAdvance是Phaser中一個重要實現阻塞的API,其實arriveAndAwaitAdvance是由arrive方法和awaitAdvance方法合并而來,兩個方法的作用分别為

  • arrive:到達屏障但不阻塞,返回值為到達的階段号。
  • awaitAdvance(int):接收一個 int 值的階段号,在指定的屏障處阻塞。

測試代碼如下

publicclassPhaserTestArrive{ publicstaticRandomrandom=newRandom(System.currentTimeMillis()); publicstaticvoidmain(String[]args){ Phaserphaser=newPhaser(5); for(inti=0;i<5;i ){ newTask(i,phaser).start(); } phaser.register(); //主線程需要調用arrive的原因是主線程注冊的第六個線程還未到達,需要手動到達,才能調用awaitAdvance阻塞屏障 phaser.arrive(); //因為Phaser線程數為6,所以即使5個線程已經到達,但是還差主線程的一個,目前階段數就是0 phaser.awaitAdvance(0); System.out.println("alltaskisend"); } staticclassTaskextendsThread{ Phaserphaser; publicTask(intnum,Phaserphaser){ super("Thread--" String.valueOf(num)); this.phaser=phaser; } @Override publicvoidrun(){ try{ System.out.println(Thread.currentThread().getName() "===task1isstart"); TimeUnit.SECONDS.sleep(random.nextInt(3)); System.out.println(Thread.currentThread().getName() "===task1isend"); //到達且不等待 phaser.arrive(); System.out.println(Thread.currentThread().getName() "===task2isstart"); TimeUnit.SECONDS.sleep(random.nextInt(3)); System.out.println(Thread.currentThread().getName() "===task2isend"); }catch(InterruptedExceptione){ e.printStackTrace(); } } } }

中斷響應

我們需要特别注意的就是Phaser所有API中隻有awaitAdvanceInterruptibly是響應中斷的,其餘全部不會響應中斷所以不需要對其進行異常處理,演示如下

publicstaticvoidmain(String[]args){ Phaserphaser=newPhaser(3); ThreadT1=newThread(()->{ try{ phaser.awaitAdvanceInterruptibly(phaser.getPhase()); }catch(InterruptedExceptione){ System.out.println("中斷異常"); e.printStackTrace(); } //phaser.arriveAndAwaitAdvance(); }); T1.start(); T1.interrupt(); phaser.arriveAndAwaitAdvance(); }

ase緩存機制(Phaser并發階段器)4

,
Comments
Welcome to tft每日頭條 comments! Please keep conversations courteous and on-topic. To fosterproductive and respectful conversations, you may see comments from our Community Managers.
Sign up to post
Sort by
Show More Comments
推荐阅读
玉娆和甄母現實中是親生母女嗎(為什麼隻有三小姐玉娆赢得了最終的幸福)
玉娆和甄母現實中是親生母女嗎(為什麼隻有三小姐玉娆赢得了最終的幸福)
  #頭條創作挑戰賽##我在頭條搞創作##甄嬛傳#   甄家一共三個女兒,長女甄嬛為了妹妹的家族的前途進宮選秀,成為帝王無數個女人當中的一個。次女浣碧乃是見不得光的私生子,耍了陰狠手段讓自己如願嫁給了果郡王,奈何對方心裡完全沒有她。隻有第三個女兒玉娆,她拒絕了皇帝的心意,依照自己的本心嫁給了心愛的男人慎貝勒。   為什麼甄家三姝裡隻有最小的玉娆得到了幸...
2025-04-22
董姓的新版排名(董姓最新排名出來了)
董姓的新版排名(董姓最新排名出來了)
  在最新出版的《中國四百大姓》一書中,繪制出了中國400個大姓的“姓氏地圖”。書中的姓氏地圖是一種頻率圖,也就是某姓氏人數在地區總人口中所占比例的示意圖,在較長的時期内,中國人的姓氏地理分布變化不會太大。由于400個大姓約占我國總人口的97%,這也代表了我國絕大多數人口的姓氏地圖。      《中國四百大姓(套裝共3冊)》主要分析了中國姓氏的特點以及400...
2025-04-22
巨龍咆哮卡組(巨龍咆哮龍牧歸來)
巨龍咆哮卡組(巨龍咆哮龍牧歸來)
  新版本中,牧師大部分都以恩佐斯環牧和克蘇恩牧的形态征戰天梯,而最近歐服玩家用一套微調過的龍牧卡組成功登頂歐服第一,讓我們一起來看看吧~      【卡組詳情】      【卡牌選擇】   這套牌在傳統龍牧的基礎上增加了2張古神新卡,禁忌畸變和變幻之影。   禁忌畸變:很多人覺得這張卡随機性太大,但是相對的在龍牧卡組中這張牌能帶來更多的變化。當你手中卡着一...
2025-04-22
有話說随遇而安(小議随遇而安)
有話說随遇而安(小議随遇而安)
  #情感點評大賞#   文/王民官      《幽窗小記》中有這樣一副對聯:“寵辱不驚,閑看庭前花開花落。去留無意,漫随天外雲卷雲舒。”這句話的意思是說,為人做事能視寵辱如花開花落般平常,才能不驚;視職位去留如雲卷雲舒般變幻,才能無意。   看起來,人生雖然存滿了荊棘與挫折,教訓與失敗,但拿這句話一對照,就瞬時感覺天空飄來五個字:那都不叫事。“是事兒也就煩...
2025-04-22
十大忌諱你知道哪些(用詞這樣百無晉忌)
十大忌諱你知道哪些(用詞這樣百無晉忌)
     “有醋可吃糠,無醋肉不香。”   —— 探員手記   歡迎加入遣詞造句1班(太原群),今天我們将教授四字詞語,零基礎同學也可以參加,晉言晉語,包教包會。   不用再擔心鍵盤上的H鍵被磨平棱角,聊天隻會發紅紅火火恍恍惚惚。   本期為你準備了太原文化人都在用的四字詞語,它們都有哪些?适合在什麼場合用?   快跟上節奏解鎖新技能,把它們打包進肚吧。  ...
2025-04-22
Copyright 2023-2025 - www.tftnews.com All Rights Reserved