CountDownLatch 瞬間炸裂!同基於 AQS,憑什麼 CyclicBarrier 可以這麼秀?

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"前言"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看完 CountDownLatch 正準備表示一番,突然看到了一個 CyclicBarrier —— 迴環屏障。沃特?迴環還屏障?說比 CountDownLatch 要多一個迴環,那咱可得瞧一瞧,看一看了!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"公衆號:liuzhihangs,記錄工作學習中的技術、開發及源碼筆記;時不時分享一些生活中的見聞感悟。歡迎大佬來指導!"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"介紹"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一個同步輔助,它允許一組線程的所有等待彼此達成共同屏障點。 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CyclicBarrier 在涉及固定線程數且必須等待彼此的程序非常有用。 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"該屏障被稱爲迴環屏障 ,因爲它在等待的線程被釋放後可以被重新利用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"CyclicBarrier 支持一個可選的 Runnable 命令,該命令在障礙中的最後一個線程到達之後,但在釋放任何線程之前,每個屏障點運行一次。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"此屏障操作對於在任何一方繼續之前更新共享狀態很有用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過上面的源碼註釋基本可以得出以下結論:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"CyclicBarrier 和 CountDownLatch 類似,但它是一組線程等待,直到在其他線程中執行的一組操作完成爲止。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"CountDownLatch 是計數遞減,結束後再調用 await 或者 countdown 都會立即返回,但是 CyclicBarrier 可以重置屏障。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"CyclicBarrier 還可以傳入參數 Runnable ,Runnable 會在釋放線程之前執行。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"基本使用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"既然上面總結了三個結論,下面當然從三個方面演示如何使用的:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"- 屏障功能"},{"type":"text","text":" "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class CyclicBarrierTest {\n\n private static final CyclicBarrier CYCLIC_BARRIER = new CyclicBarrier(11);\n\n public static void main(String[] args) throws BrokenBarrierException, InterruptedException {\n\n ExecutorService pool = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.MILLISECONDS,\n new LinkedBlockingQueue<>(1024),\n new ThreadFactoryBuilder().setNameFormat(\"Thread-pool-%d\").build(),\n new ThreadPoolExecutor.AbortPolicy());\n\n for (int i = 0; i < 10; i++) {\n\n pool.submit(() -> {\n\n try {\n System.out.println(Thread.currentThread().getName() + \" 開始執行\");\n Thread.sleep(5000);\n System.out.println(Thread.currentThread().getName() + \" 執行結束,準備調用 await\");\n CYCLIC_BARRIER.await();\n } catch (InterruptedException | BrokenBarrierException e) {\n e.printStackTrace();\n }\n\n });\n\n }\n\n System.out.println(\"主線程執行 —————————————— >>>\");\n\n CYCLIC_BARRIER.await();\n\n System.out.println(\"主線程繼續執行 —————————————— >>>\");\n\n pool.shutdown();\n\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過上面代碼其實模擬了個類似 CountDownLatch 的功能,讓所有線程等待,直到都調用 await 之後,各個線程繼續執行,同時主線程也繼續往下執行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不過相對 CountDownLatch 的指定一個線程或多個等待,直到其他線程執行結束,等待的線程才繼續執行來說,CyclicBarrier 相對來說還是遜色。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"差別總結如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"CountDownLatch 是指定等待的線程,其他線程進行 countDown,等計數爲 0 時,等待的線程繼續執行。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"CyclicBarrier 是一組線程調用 await 進行等待,當所有的都進入等待的時候,這一組就會一起衝破屏障繼續執行。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"- 迴環功能"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class CyclicBarrierTest2 {\n\n private static final CyclicBarrier CYCLIC_BARRIER = new CyclicBarrier(5);\n\n public static void main(String[] args) throws BrokenBarrierException, InterruptedException {\n \n ExecutorService pool = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,\n new LinkedBlockingQueue<>(1024),\n new ThreadFactoryBuilder().setNameFormat(\"Thread-pool-%d\").build(),\n new ThreadPoolExecutor.AbortPolicy());\n\n for (int i = 0; i < 5; i++) {\n\n pool.submit(() -> {\n\n try {\n System.out.println(Thread.currentThread().getName() + \" 開始執行\");\n CYCLIC_BARRIER.await();\n\n System.out.println(Thread.currentThread().getName() + \" 衝破屏障 >>> 1\");\n CYCLIC_BARRIER.await();\n\n System.out.println(Thread.currentThread().getName() + \" 衝破屏障 >>>>> 2\");\n CYCLIC_BARRIER.await();\n } catch (InterruptedException | BrokenBarrierException e) {\n e.printStackTrace();\n }\n\n });\n\n }\n\n pool.shutdown();\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/52/52c50ac7bc471ad40ba1673456a5a110.png","alt":"carbon-gzpBD4","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面演示的迴環的用法。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"- 迴環 Runnable"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這塊只需要在聲明的 CyclicBarrier 修改爲以下即可:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private static final CyclicBarrier CYCLIC_BARRIER = new CyclicBarrier(5, new Runnable() {\n @Override\n public void run() {\n System.out.println(\"執行一次 Runnable \");\n }\n});"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"打印結果如下:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/40/40d79c019eff762ea3d5a797b5bb7e8b.png","alt":"carbon1-lHnKnA","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"可以看出只是在下一個計數開始之前,先執行 Runnable 。至於是不是在釋放屏障之前,那很容易,直接 Debug 走一遭就知道了!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過 debug 可以看出"},{"type":"text","marks":[{"type":"strong"}],"text":"Runnable 會在釋放線程之前執行"},{"type":"text","text":"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"問題疑問?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"CyclicBarrier 和 AQS 有什麼關係?"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"CyclicBarrier 的實現原理是什麼?"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"CyclicBarrier 是如何實現迴環的?"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面就帶着疑問去源碼閱讀,一探究竟!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"源碼分析"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"基本結構"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/37/3799aaf85797e1c92943b6c8806f5cae.png","alt":"CleanShot-2020-09-12-KFzaCR0G@2x-seVhre","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過 UML 乍一看,CyclicBarrier 和 AQS 並無什麼關係,那下面開始從"},{"type":"text","marks":[{"type":"strong"}],"text":"參數"},{"type":"text","text":"、*"},{"type":"text","marks":[{"type":"italic"}],"text":"構造器"},{"type":"text","text":"*、"},{"type":"text","marks":[{"type":"strong"}],"text":"await()方法"},{"type":"text","text":"分別看源碼。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"參數"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public class CyclicBarrier {\n\n /**\n * 屏障的每次使用都表示爲一個生成實例。\n * broken 表示屏障是否被打破。\n */\n private static class Generation {\n boolean broken = false;\n }\n\n /** 鎖 */\n private final ReentrantLock lock = new ReentrantLock();\n /** 條件等待,直到屏障 */\n private final Condition trip = lock.newCondition();\n /** 等待計數 */\n private final int parties;\n /* The command to run when tripped */\n private final Runnable barrierCommand;\n /** 當前 generation 新創建的*/\n private Generation generation = new Generation();\n /** 仍在等待的 parties 數量,遞減 爲 0 會重置 */\n private int count; \n}\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過上面可以看出:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"內部使用了一個靜態類 Generation ,它有什麼功能呢?通過註釋瞭解到,每次使用屏障的時候都會生成,具體有什麼用,其實就是用來標示屏障是否被打破。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"內部還有一個 parties 表示等待計數,count 表示仍在等待的計數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"那就繼續往下看吧!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"構造器"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public CyclicBarrier(int parties, Runnable barrierAction) {\n if (parties <= 0) throw new IllegalArgumentException();\n this.parties = parties;\n this.count = parties;\n this.barrierCommand = barrierAction;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這裏的入參有兩個:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"parties(等待計數):記錄多少個線程調用 await 之後,纔會一起打破屏障。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"barrierAction:衝破屏障前執行的行爲。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是會同時對 parties 和 count 賦值爲傳入的 parties。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單參數構造,其實就是將 barrierAction 賦值爲 null。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"await() 方法"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在示例中用的 "},{"type":"text","marks":[{"type":"strong"}],"text":"await()"},{"type":"text","text":" 方法, 那就從 "},{"type":"text","marks":[{"type":"strong"}],"text":"await()"},{"type":"text","text":" 方法入手:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"public int await() throws InterruptedException, BrokenBarrierException {\n try {\n return dowait(false, 0L);\n } catch (TimeoutException toe) {\n throw new Error(toe); // cannot happen\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"await() 纔是重頭戲, 先來根據源碼註釋,瞭解是幹嘛的,看看作者怎麼講:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"等到所有各方都在此障礙上調用await。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"如果當前線程不是最後到達的線程,則出於線程調度目的將其禁用,並使其處於休眠狀態,直到發生以下情況之一:"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 1. 最後一個線程到達;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 2. 其他一些線程中斷當前線程;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 3. 其他一些線程中斷其他正在等待的線程之一;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 4. 等待屏障的時候其他線程超時;"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 5. 其他一些線程在此屏障上調用 reset。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"看到這些,咱們最想看的當然是 2.1 ,等待最後一個線程到達屏障,之後所有的線程一起繼續執行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\nprivate int dowait(boolean timed, long nanos)\n throws InterruptedException, BrokenBarrierException,\n TimeoutException {\n \n // 加鎖\n final ReentrantLock lock = this.lock;\n lock.lock();\n try {\n\n // 在這裏用到了這個代\n final Generation g = generation;\n\n if (g.broken)\n throw new BrokenBarrierException();\n // 線程終中斷標示\n if (Thread.interrupted()) {\n breakBarrier();\n throw new InterruptedException();\n }\n\n // 對計數進行遞減\n int index = --count;\n // 如果是 0 則\n if (index == 0) { // tripped\n boolean ranAction = false;\n try {\n final Runnable command = barrierCommand;\n // 不是 null 先執行行爲\n if (command != null)\n // 這裏不是新開線程\n command.run();\n ranAction = true;\n // 下一代\n nextGeneration();\n return 0;\n } finally {\n // 任務未成功時,即 ranAction 還是 false 打破屏障\n if (!ranAction)\n breakBarrier();\n }\n }\n\n // loop until tripped, broken, interrupted, or timed out\n // 自旋\n for (;;) {\n try {\n // 沒有設置超時時間\n if (!timed)\n // 進入等待\n trip.await();\n else if (nanos > 0L)\n nanos = trip.awaitNanos(nanos);\n } catch (InterruptedException ie) {\n if (g == generation && ! g.broken) {\n breakBarrier();\n throw ie;\n } else {\n Thread.currentThread().interrupt();\n }\n }\n\n if (g.broken)\n throw new BrokenBarrierException();\n // 已經下一代了\n if (g != generation)\n return index;\n\n if (timed && nanos <= 0L) {\n breakBarrier();\n throw new TimeoutException();\n }\n }\n } finally {\n lock.unlock();\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這一大坨代碼,完全沒有看的慾望,直接划過去吧!"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以…… 直接看到了這裏吧。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"代碼還是要閱讀的,分開來看(異常流程省略):"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"使用了 ReentrantLock 互斥鎖,因此對 count、broken 的修改是原子性的。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"對 count 進行 --count 操作,這樣就理解爲什麼說 count 是仍在等待的計數,或者說還有多少才能到達屏障點。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"當 count 爲 0 ,表示到達屏障點了"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 1. "}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/d7/d7cbd64040be2331da00904952b88f7a.png","alt":"cyclicbarrier-amQMu4","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 2. command 不爲 null,會先執行 "},{"type":"text","marks":[{"type":"strong"}],"text":"command.run()"},{"type":"text","text":", 值得注意的是這裏並不是新開了個線程。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 3. "},{"type":"text","marks":[{"type":"strong"}],"text":"nextGeneration()"},{"type":"text","text":"開始新的下一代,即重置 count 爲 parties。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 4. 在 finally 裏面使用 "},{"type":"text","marks":[{"type":"strong"}],"text":"breakBarrier()"},{"type":"text","text":" 打破屏障。"}]},{"type":"numberedlist","attrs":{"start":"4","normalizeStart":"4"},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"當 count 不是 0"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 1. 自旋,直到是 0."}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這後面還有兩個方法不能少:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"private void nextGeneration() {\n // 喚醒線程\n trip.signalAll();\n // 更新 count 爲 parties\n count = parties;\n // 更新 Generation\n generation = new Generation();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"// 打破屏障,並喚醒全部\nprivate void breakBarrier() {\n generation.broken = true;\n count = parties;\n trip.signalAll();\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"reset()"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"\npublic void reset() {\n final ReentrantLock lock = this.lock;\n lock.lock();\n try {\n breakBarrier(); // break the current generation\n nextGeneration(); // start a new generation\n } finally {\n lock.unlock();\n }\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"將屏障重置爲其初始狀態,reset() 方法其實還是調用的 breakBarrier() 和 nextGeneration(),前者時打破當前代,後者是開始新的一輪。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"總結"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Q: CyclicBarrier 和 AQS 有什麼關係?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"A:"},{"type":"text","text":" 通過閱讀源碼,其實發現是使用了 ReentrantLock 互斥鎖 以及 Condition 的等待喚醒功能。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Q: CyclicBarrier 的實現原理是什麼?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"A:"},{"type":"text","text":" 內部含有兩個計數,分別是 parties 和 count ,初始是二者相等,當有線程調用 await() 時,count 遞減,只要 count 不爲 0 , 就會阻塞線程,直到 count 遞減爲 0 時,此時會所有線程一起釋放,同時將 count 重置爲 parties。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Q: CyclicBarrier 是如何實現迴環的?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"A:"},{"type":"text","text":" 使用兩個計數,count 遞減,當 count 爲 0 時,會重置爲 parties,從而達到迴環效果。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"Q: 爲什麼 count 的 --count 操作沒有使用 CAS?"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"A:"},{"type":"text","text":" 因爲已經 lock.lock() 了,使用了 ReentrantLock 鎖能夠保證 count 的原子性。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"CyclicBarrier 和 CountDownLatch 的區別"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"迴環:CyclicBarrier 可以迴環,重新計數。CountDownLatch 只能一輪。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"計數器:CyclicBarrier 的計數器自己維護遞減, CountDownLatch 的計數器維護則是交給使用者。"}]}]},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"阻塞線程:CyclicBarrier 阻塞的是自身,當到達屏障後,所有被阻塞的線程一起釋放。CountDownLatch 可以指定阻塞線程。"}]}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":4},"content":[{"type":"text","text":"結束語"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文主要介紹了 CyclicBarrier 的常用方式,通過源碼方式,分析如何達到屏障以及迴環的效果。不對之處,請多指正。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章