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}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章