CountDownLatch和CyclicBarrier和Semaphore最通俗形象解释
应该还有好多同学对这三个的区别比较模糊,网络上其他文章说的也比较专业化。所以我在这里举个例子说明这三个的区别。
我们假定有一场百米比赛,比赛包括十个运动员和一个裁判,每个运动员和每个裁判都是一个线程,那么:
CountDownLatch应用场景:教练需要在终点等待运动员,等所有运动员都达到终点了才可以宣布比赛结束
private class CountDownLatchTask implements Runnable {
private final CountDownLatch countDownLatch;
private CountDownLatchTask(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +":起跑");
try {
Thread.sleep(Math.round(Math.floor(Math.random()*3000)));
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() +":我在奔跑过程中被打断了");
}
System.out.println(Thread.currentThread().getName() +":到达终点");
countDownLatch.countDown();
}
}
@Test
public void TestCountDownWatch() {
final int threadCount = 10;
System.out.println("裁判:啪!比赛开始");
final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(new CountDownLatchTask(countDownLatch), "运动员" + i).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
System.out.println("裁判:等待比赛结果过程中我被打断了");
}
System.out.println("裁判:比赛结束");
}
我们定义了一个CountDownLatch,相当于一个计数器,计数大小当然和运动员数量是一致的。所有运动员起跑后,教练则等待计数器变为0(执行CountDownLatch.await),运动员到达终点后会将这个计数器-1(执行CountDownLatch.countDown)。当计数器变为0时教练等待结束宣布比赛结束。
裁判:啪!比赛开始
运动员0:起跑
运动员1:起跑
运动员3:起跑
运动员5:起跑
运动员7:起跑
运动员2:起跑
运动员9:起跑
运动员6:起跑
运动员4:起跑
运动员8:起跑
运动员1:到达终点
运动员2:到达终点
运动员9:到达终点
运动员7:到达终点
运动员6:到达终点
运动员0:到达终点
运动员8:到达终点
运动员5:到达终点
运动员4:到达终点
运动员3:到达终点
裁判:比赛结束
CyclicBarrier应用场景:这不是一场普通的比赛,要求所有运动员跑到80m的地方先等待,直到所有的运动员都到达80m的时候再重新起跑,直到到达终点
private class CyclicBarrierTask implements Runnable {
private final CyclicBarrier cyclicBarrier;
private CyclicBarrierTask(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +":起跑");
try {
Thread.sleep(Math.round(Math.floor(Math.random()*3000)));
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() +":我在奔跑过程中被打断了");
}
System.out.println(Thread.currentThread().getName() +":到达80米处");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() +":我在等待过程中被打断了");
} catch (BrokenBarrierException e) {
System.out.println(Thread.currentThread().getName() +":我被通知不需要等待了");
}
try {
Thread.sleep(Math.round(Math.floor(Math.random()*1000)));
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() +":我在奔跑过程中被打断了");
}
System.out.println(Thread.currentThread().getName() +":到达终点");
}
}
@Test
public void TestCyclicBarrier() throws InterruptedException {
final int threadCount = 10;
System.out.println("裁判:啪!比赛开始");
final CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(new CyclicBarrierTask(cyclicBarrier), "运动员" + i).start();
}
Thread.sleep(5000);
System.out.println("裁判:哦哦,比赛结束了啊");
}
我们定义了一个CyclicBarrier,它也相当于一个计数器,计数大小等于运动员人数。当运动员到达80米开始等待其他运动员(执行CyclicBarrier.await,CyclicBarrier在执行await的时候会将内部的计数器-1),直到所有运动员到达80米时计数器变为0则所有运动员解除等待重新起跑,直到跑到终点。
裁判:啪!比赛开始
运动员0:起跑
运动员4:起跑
运动员6:起跑
运动员7:起跑
运动员1:起跑
运动员2:起跑
运动员3:起跑
运动员5:起跑
运动员9:起跑
运动员8:起跑
运动员2:到达80米处
运动员3:到达80米处
运动员7:到达80米处
运动员5:到达80米处
运动员0:到达80米处
运动员4:到达80米处
运动员9:到达80米处
运动员6:到达80米处
运动员8:到达80米处
运动员1:到达80米处
运动员5:到达终点
运动员4:到达终点
运动员9:到达终点
运动员1:到达终点
运动员6:到达终点
运动员7:到达终点
运动员2:到达终点
运动员8:到达终点
运动员3:到达终点
运动员0:到达终点
裁判:哦哦,比赛结束了啊
Semaphore应用场景:这也不是一场普通的比赛,共有10个运动员参加比赛但是却只有3个赛道,所以没有抢到赛道的运动员只能等到赛道上的运动员跑完后再起跑。
private class SemaphoreTask implements Runnable {
private final Semaphore semaphore;
private SemaphoreTask(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() +":裁判,有跑道了吗?");
boolean canStart = semaphore.tryAcquire();
if (!canStart) {
System.out.println(Thread.currentThread().getName() +":那我再等等");
try {
semaphore.acquire();
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() +":我在等待过程中被打断了");
}
}
System.out.println(Thread.currentThread().getName() +":起跑");
try {
Thread.sleep(Math.round(Math.floor(Math.random()*3000)));
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() +":我在奔跑过程中被打断了");
}
System.out.println(Thread.currentThread().getName() +":到达终点");
semaphore.release();
}
}
@Test
public void TestSemaphore() throws InterruptedException {
final int threadCount = 10;
final int semaphoreCount = 3;
System.out.println("裁判:啪!比赛开始");
final Semaphore semaphore = new Semaphore(semaphoreCount);
for (int i = 0; i < threadCount; i++) {
new Thread(new SemaphoreTask(semaphore), "运动员" + i).start();
}
Thread.sleep(15000);
System.out.println("裁判:哦哦,比赛结束了啊");
}
我们定义了一个Semaphore作为赛道,运动员可以询问有赛道了吗并立即获得应答(执行Semaphore.tryAcquire),也可以直接申请赛道并等待(执行Semaphore.acquire),直到获取赛道再起跑。运动员跑到终点后要主动释放报道(执行Semaphore.release),以便其他运动员申请跑到。
裁判:啪!比赛开始
运动员0:裁判,有跑道了吗?
运动员1:裁判,我可以跑了吗?
运动员1:起跑
运动员2:裁判,我可以跑了吗?
运动员2:起跑
运动员0:起跑
运动员4:裁判,我可以跑了吗?
运动员4:那我再等等
运动员5:裁判,我可以跑了吗?
运动员5:那我再等等
运动员6:裁判,我可以跑了吗?
运动员3:裁判,我可以跑了吗?
运动员3:那我再等等
运动员8:裁判,我可以跑了吗?
运动员8:那我再等等
运动员6:那我再等等
运动员9:裁判,我可以跑了吗?
运动员7:裁判,我可以跑了吗?
运动员9:那我再等等
运动员7:那我再等等
运动员0:到达终点
运动员4:起跑
运动员4:到达终点
运动员5:起跑
运动员2:到达终点
运动员3:起跑
运动员1:到达终点
运动员8:起跑
运动员5:到达终点
运动员6:起跑
运动员8:到达终点
运动员9:起跑
运动员9:到达终点
运动员7:起跑
运动员7:到达终点
运动员6:到达终点
运动员3:到达终点
裁判:哦哦,比赛结束了啊
总结:
- CountDownLatch:1. 是谁在等待?是裁判在等待。2.为什么要等待?要等待所有运动员到终点才能宣布比赛结束。
- CyclicBarrier:1. 是谁在等待?是运动员在等待。2.为什么要等待?要等所有运动员都到80米处才可以继续跑。
- Semaphore1. 是谁在等待?是运动员在等待。2.为什么要等待?要有空余赛道的时候才可以起跑。
其他:
- CountDownLatch和CyclicBarrier的await方法还有一种重载形式:
public boolean await(long timeout, TimeUnit unit)
即执行等待的时候设置等待时间,超过此时间则放弃等待执行后续代码。
- CountDownLatch不可重用,CyclicBarrier是可重用的
- CyclicBarrier的构造方法还有一种重载形式:
public CyclicBarrier(int parties, Runnable barrierAction)
当CyclicBarrier的计数器减至0的时候,会直接调用barrierAction的run方法,注意:是直接调用barrierAction的run方法,而不是建立一个新的线程执行barrierAction,详见源码片段:
final Runnable command = barrierCommand;
if (command != null)
command.run(); - Semaphore的tryAccquire也有其他重载形式:
public boolean tryAcquire(); // 申请一个许可(跑道)并立即返回
public boolean tryAcquire(long timeout, TimeUnit unit); // 申请一个许可并等待一段时间,要么申请到了返回true,要么等待超时了返回false
public boolean tryAcquire(int permits); // 申请多个个许可(跑道)并立即返回
public boolean tryAcquire(int permits, long timeout, TimeUnit unit); // 申请多个许可并等待一段时间,要么申请到了返回true,要么等待超时了返回falseSemaphore的accquire也有其他重载形式:
public void acquire(int permits) // 申请多个许可(跑道)并一直等待下去
Semaphore的release也有其他重载形式:
public void release(int permits) // 释放多个许可(跑道)
CountDownLatch和CyclicBarrier和Semaphore最通俗形象解释的更多相关文章
- Java并发编程:CountDownLatch、CyclicBarrier和Semaphore
Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch ...
- 并发工具类:CountDownLatch、CyclicBarrier、Semaphore
在多线程的场景下,有些并发流程需要人为来控制,在JDK的并发包里提供了几个并发工具类:CountDownLatch.CyclicBarrier.Semaphore. 一.CountDownLatch ...
- Java并发(8):CountDownLatch、CyclicBarrier、Semaphore、Callable、Future
CountDownLatch.CyclicBarrier.Semaphore.Callable.Future 都位于java.util.concurrent包下,其中CountDownLatch.C ...
- 【Java多线程】JUC包下的工具类CountDownLatch、CyclicBarrier和Semaphore
前言 JUC中为了满足在并发编程中不同的需求,提供了几个工具类供我们使用,分别是CountDownLatch.CyclicBarrier和Semaphore,其原理都是使用了AQS来实现,下面分别进行 ...
- CountDownLatch、CyclicBarrier和Semaphore
转载:http://www.cnblogs.com/dolphin0520/p/3920397.html 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDown ...
- 使用Java辅助类(CountDownLatch、CyclicBarrier、Semaphore)并发编程
在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我们就来学习一下这三个辅助类的用法 一.C ...
- Java并发之CountDownLatch、CyclicBarrier和Semaphore
CountDownLatch 是能使一组线程等另一组线程都跑完了再继续跑:CyclicBarrier 能够使一组线程在一个时间点上达到同步,可以是一起开始执行全部任务或者一部分任务. CountDow ...
- CountDownLatch、CyclicBarrier、Semaphore共同之处与区别以及各自使用场景
区别 CountDownLatch 使一个线程A或是组线程A等待其它线程执行完毕后,一个线程A或是组线程A才继续执行.CyclicBarrier:一组线程使用await()指定barrier,所有线程 ...
- Thread.join(), CountDownLatch、CyclicBarrier和 Semaphore区别,联系及应用
在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我们就来学习一下这三个辅助类的用法, 由于 ...
随机推荐
- L2-002. 链表去重(模拟)
题意: 给定一个带整数键值的单链表L,本题要求你编写程序,删除那些键值的绝对值有重复的结点.即对任意键值K,只有键值或其绝对值等于K的第一个结点可以被保留.同时,所有被删除的结点必须被保存在另外一个链 ...
- 当3D打影人头”成为黑客的秘密武器,隐私该如何保护?
在<碟中谍>系列电影中,除了超级敬业又帅气的阿汤哥之外,最让人津津乐道的桥段就是用3D打印做出来的"人头".通过这些惟妙惟肖的"人头",阿汤哥完成了 ...
- linux7下nenux3.14的maven私服搭建和配置使用
为什么用私服,各种帖子很多,话不多说,直接奉上本博主的搭建过程. 一.环境准备:确保jdk和maven安装完成 二.下载nenux:地址:https://www.sonatype.com/downlo ...
- swing开发图形界面工具配置(可自由拖控件上去)
swing开发图形界面工具,eclipse swing图形化操作界面工具配置 1.有一个小功能要有一个界面,之前知道有一个 图形化界面的(就是可以往上面拖控件布局的工具)JBuilder,今天上午就下 ...
- Concurrent包下用过哪些类?
1.executor接口,使用executor接口的子接口ExecutorService用来创建线程池2.Lock接口下的ReentrantLock类,实现同步,比如三个线程循环打印ABCABCABC ...
- PHP mb_substr mbstring 函数
定义和用法 mb_substr - 获取部分字符串 版本支持 PHP4 PHP5 PHP7 支持 支持 支持 5.4.8 length 传入 NULL,则从 start 提取到字符串的结尾处. 在之前 ...
- 013.CI4框架CodeIgniter数据库操作之:查询数据库,并让数据以数组的方式返回查询结果
01. 我们在CI4框架中的Model文件夹新建一个User_model.php的文件,使用的是getResultArray,表示并让数据以数组的方式返回查询结果,代码如下: <?php nam ...
- 数据结构——Java Stack 类
定义 栈是Vector的一个子类,它实现了一个标准的后进先出的栈.堆栈只定义了默认构造函数,用来创建一个空栈. 堆栈除了包括由Vector定义的所有方法,也定义了自己的一些方法. 图例 在下面图片中可 ...
- Spark的任务提交和执行流程概述
1.概述 为了更好地理解调度,我们先看一下集群模式的Spark程序运行架构图,如上所示: 2.Spark中的基本概念 1.Application:表示你的程序 2.Driver:表示main函数,创建 ...
- Makefile入门1
Linux中的编译脚本Makefile的讲解设计 概念 编译控制脚本(.c.h----->bin) Makefile最终要的是清晰编译链接的整个过程 Makefile的优化设计 工作原理 mak ...