在 JUC 下包含了一些常用的同步工具类,今天就来详细介绍一下,CountDownLatch,CyclicBarrier,Semaphore 的使用方法以及它们之间的区别。
一、CountDownLatch
先看一下,CountDownLatch 源码的官方介绍。
图片
意思是,它是一个同步辅助器,允许一个或多个线程一直等待,直到一组在其他线程执行的操作全部完成。

public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);
}

它的构造方法,会传入一个 count 值,用于计数。
常用的方法有两个:

public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}public void countDown() {sync.releaseShared(1);
}

当一个线程调用await方法时,就会阻塞当前线程。每当有线程调用一次 countDown 方法时,计数就会减 1。当 count 的值等于 0 的时候,被阻塞的线程才会继续运行。
现在设想一个场景,公司项目,线上出现了一个紧急 bug,被客户投诉,领导焦急的过来,想找人迅速的解决这个 bug 。
那么,一个人解决肯定速度慢啊,于是叫来张三和李四,一起分工解决。终于,当他们两个都做完了自己所需要做的任务之后,领导才可以答复客户,客户也就消气了(没办法啊,客户是上帝嘛)。
于是,我们可以设计一个 Worker 类来模拟单个人修复 bug 的过程,主线程就是领导,一直等待所有 Worker 任务执行结束,主线程才可以继续往下走。

public class CountDownTest {static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(2);Worker w1 = new Worker("张三", 2000, latch);Worker w2 = new Worker("李四", 3000, latch);w1.start();w2.start();long startTime = System.currentTimeMillis();latch.await();System.out.println("bug全部解决,领导可以给客户交差了,任务总耗时:"+ (System.currentTimeMillis() - startTime));}static class Worker extends Thread{String name;int workTime;CountDownLatch latch;public Worker(String name, int workTime, CountDownLatch latch) {this.name = name;this.workTime = workTime;this.latch = latch;}@Overridepublic void run() {System.out.println(name+"开始修复bug,当前时间:"+sdf.format(new Date()));doWork();System.out.println(name+"结束修复bug,当前时间:"+sdf.format(new Date()));latch.countDown();}private void doWork() {try {//模拟工作耗时Thread.sleep(workTime);} catch (InterruptedException e) {e.printStackTrace();}}}
}

本来需要 5 秒完成的任务,两个人 3 秒就完成了。我只能说,这程序员的工作效率真是太太太高了。
二、CyclicBarrier
barrier 英文是屏障,障碍,栅栏的意思。cyclic是循环的意思,就是说,这个屏障可以循环使用(什么意思,等下我举例子就知道了)。源码官方解释是:
A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point . The barrier is called cyclic because it can be re-used after the waiting threads are released.
一组线程会互相等待,直到所有线程都到达一个同步点。这个就非常有意思了,就像一群人被困到了一个栅栏前面,只有等最后一个人到达之后,他们才可以合力把栅栏(屏障)突破。
CyclicBarrier 提供了两种构造方法:

public CyclicBarrier(int parties) {this(parties, null);
}
public CyclicBarrier(int parties, Runnable barrierAction) {if (parties <= 0) throw new IllegalArgumentException();this.parties = parties;this.count = parties;this.barrierCommand = barrierAction;
}

第一个构造的参数,指的是需要几个线程一起到达,才可以使所有线程取消等待。第二个构造,额外指定了一个参数,用于在所有线程达到屏障时,优先执行 barrierAction。
现在模拟一个常用的场景,一组运动员比赛 1000 米,只有在所有人都准备完成之后,才可以一起开跑(额,先忽略裁判吹口哨的细节)。
定义一个 Runner 类代表运动员,其内部维护一个共有的 CyclicBarrier,每个人都有一个准备时间,准备完成之后,会调用 await 方法,等待其他运动员。当所有人准备都 OK 时,就可以开跑了。

public class BarrierTest {public static void main(String[] args) {CyclicBarrier barrier = new CyclicBarrier(3);  //①Runner runner1 = new Runner(barrier, "张三");Runner runner2 = new Runner(barrier, "李四");Runner runner3 = new Runner(barrier, "王五");ExecutorService service = Executors.newFixedThreadPool(3);service.execute(runner1);service.execute(runner2);service.execute(runner3);service.shutdown();}}class Runner implements Runnable{private CyclicBarrier barrier;private String name;public Runner(CyclicBarrier barrier, String name) {this.barrier = barrier;this.name = name;}@Overridepublic void run() {try {//模拟准备耗时Thread.sleep(new Random().nextInt(5000));System.out.println(name + ":准备OK");barrier.await();System.out.println(name +": 开跑");} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e){e.printStackTrace();}}
}

可以看到,我们已经实现了需要的功能。但是,有的同学就较真了,说你这不行啊,哪有运动员都准备好之后就开跑的,你还把裁判放在眼里吗,裁判不吹口哨,你敢跑一个试试。
好吧,也确实是这样一个理儿,那么,我们就实现一下,让裁判吹完口哨之后,他们再一起开跑吧。
这里就要用到第二个构造函数了,于是我把代码 ① 处稍微修改一下。

CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {@Overridepublic void run() {try {System.out.println("等裁判吹口哨...");//这里停顿两秒更便于观察线程执行的先后顺序Thread.sleep(2000);System.out.println("裁判吹口哨->>>>>");} catch (InterruptedException e) {e.printStackTrace();}}
});

执行结果:
张三:准备OK
李四:准备OK
王五:准备OK
等裁判吹口哨…
裁判吹口哨->>>>>
张三: 开跑
李四: 开跑
王五: 开跑
可以看到,虽然三个人都已经准备 OK了,但是,只有裁判吹完口哨之后,他们才可以开跑。
刚才,提到了循环利用是怎么体现的呢。我现在把屏障值改为 2,然后增加一个“赵六” 一起参与赛跑。被修改的部分如下:
图片
此时观察,打印结果:
张三:准备OK
李四:准备OK
等裁判吹口哨…
裁判吹口哨->>>>>
李四: 开跑
张三: 开跑
王五:准备OK
赵六:准备OK
等裁判吹口哨…
裁判吹口哨->>>>>
赵六: 开跑
王五: 开跑
发现没,可以分两批,第一批先跑两个人,然后第二批再跑两个人。也就实现了屏障的循环使用。
三、Semaphore
Semaphore 信号量,用来控制同一时间,资源可被访问的线程数量,一般可用于流量的控制。
打个比方,现在有一段公路交通比较拥堵,那怎么办呢。此时,就需要警察叔叔出面,限制车的流量。
比如,现在有 20 辆车要通过这个地段, 警察叔叔规定同一时间,最多只能通过 5 辆车,其他车辆只能等待。只有拿到许可的车辆可通过,等车辆通过之后,再归还许可,然后把它发给等待的车辆,获得许可的车辆再通行,依次类推。

public class SemaphoreTest {private static int count = 20;public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(count);//指定最多只能有五个线程同时执行Semaphore semaphore = new Semaphore(5);Random random = new Random();for (int i = 0; i < count; i++) {final int no = i;executorService.execute(new Runnable() {@Overridepublic void run() {try {//获得许可semaphore.acquire();System.out.println(no +":号车可通行");//模拟车辆通行耗时Thread.sleep(random.nextInt(2000));//释放许可semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}});}executorService.shutdown();}
}

打印结果我就不写了,需要读者自行观察,就会发现,第一批是五个车同时通行。然后,后边的车才可以依次通行,但是同时通行的车辆不能超过 5 辆。
细心的读者,就会发现,这许可一共就发 5 个,那等第一批车辆用完释放之后, 第二批的时候应该发给谁呢?
这确实是一个问题。所有等待的车辆都想先拿到许可,先通行,怎么办。这就需要,用到锁了。就所有人都去抢,谁先抢到,谁就先走呗。
我们去看一下 Semaphore的构造函数,就会发现,可以传入一个 boolean 值的参数,控制抢锁是否是公平的。

public Semaphore(int permits) {sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

默认是非公平,可以传入 true 来使用公平锁。(锁的机制是通过AQS,现在不细讲,等我以后更新哦)
这里简单的说一下,什么是公平非公平吧。
公平的话,就是你车来了,就按照先来后到的顺序正常走就行了。不公平的,也许就是,某位司机大哥膀大腰圆,手戴名表,脖子带粗金项链。别人一看惹不起,我还躲不起吗,都给这位大哥让道。你就算车走在人家前面了,你也不敢跟人家抢啊。
最后,那就是他先拿到许可证通行了。剩下的人再去抢,说不定又来一个,是警察叔叔的私交好友。(行吧行吧,其他司机只能一脸的生无可恋,怎么过个马路都这么费劲啊。。。)
总结
CountDownLatch 是一个线程等待其他线程, CyclicBarrier 是多个线程互相等待。
CountDownLatch 的计数是减 1 直到 0,CyclicBarrier 是加 1,直到指定值。
CountDownLatch 是一次性的, CyclicBarrier 可以循环利用。
CyclicBarrier 可以在最后一个线程达到屏障之前,选择先执行一个操作。
Semaphore ,需要拿到许可才能执行,并可以选择公平和非公平模式。

CountDownLatch,CyclicBarrier,Semaphore的使用方法以及它们之间的区别相关推荐

  1. (面经总结)一篇文章带你完整复习 Java 中并发关键字(CountDownLatch/CyclicBarrier/Semaphore/Volatile)

    文章目录 一.倒计数器:CountDownLatch 二.循环栅栏:CyclicBarrier 三.信号量:Semaphore 四.volatile 关键字的作用 一.倒计数器:CountDownLa ...

  2. JUC介绍--常用辅助类(CountDownLatch CyclicBarrier Semaphore)

    CountDownLatch减法计数器 每次有线程调用countDownLatch.countDown()数量就-1 数量减到0时,countDownLatch.await()会被唤醒,继续向下执行 ...

  3. CountDownLatch CyclicBarrier Semaphore

    CountDownLacth CountDownLacth(倒计数锁存器)到底有什么用呢?我们来看下面这个场景. 我们又有小片片要开始拍摄了,这个小片片需要5个演员来演,开演之前,导演需要这5个演员全 ...

  4. 《Java 7 并发编程指南》学习概要 (3)Semaphore, CountDownLatch, CyclicBarrier , Phaser, Exchanger...

    1.Semaphore  信号量 Semaphore(信号量)是一个控制访问多个共享资源的计数器. 当一个线程想要访问某个共享资源,首先,它必须获得semaphore.如果semaphore的内部计数 ...

  5. Java多线程系列(九):CountDownLatch、Semaphore等4大并发工具类详解

    之前谈过高并发编程系列:4种常用Java线程锁的特点,性能比较.使用场景 ,以及高并发编程系列:ConcurrentHashMap的实现原理(JDK1.7和JDK1.8) 今天主要介绍concurre ...

  6. CountDownLatch和Semaphore使用场景

    CountDownLatch CountDownLatch位于java.util.concurrent包下,利用它可以实现类似计数器的功能.比如有一个任务A,它要等到其它3任务完成才能执行,此时就可以 ...

  7. Pandas中map,applymap和apply方法之间的区别

    本文翻译自:Difference between map, applymap and apply methods in Pandas Can you tell me when to use these ...

  8. java ordered list_关于并行处理:Java 8的forEachOrdered()和sequence()方法之间的区别?...

    我正在使用Java 8并行流,并且希望以并行流的方式打印元素是某种顺序(例如插入顺序,反向顺序或顺序顺序). 为此,我尝试了以下代码: System.out.println("With fo ...

  9. 辅助方法 @Html.Raw与 HtmlString区别

    辅助方法 @Html.Raw与 HtmlString区别 //Html.Raw其实是调用 new Microsoft.AspNetCore.Html.HtmlString(xxx)@{ViewData ...

最新文章

  1. 实战:使用Nginx限流
  2. R语言笔记1:数据类型(向量、数组、矩阵、 列表和数据框)
  3. eclipse中开发python
  4. php无极分类非递归_无限极分类算法,对你一定有帮助
  5. python实现http下载文件-Python实现HTTP协议下的文件下载方法总结
  6. 浙江金融职业学院计算机一级,浙江金融职业学院全景-360度,720度,高清全景地图-expoon网展...
  7. 嵌入式linux基本指令,成都嵌入式开发之Linux常用命令大全
  8. vue 跨域:使用vue-cli 配置 proxyTable 实现跨域问题
  9. css td 溢出改为省略号
  10. mysql数据库访问组件_mysql数据库访问组件
  11. iis服务器怎么限制运行asp文件,Win2008 r2 IIS7.5制定目录禁止执行脚本的方法
  12. 基姆拉尔森计算公式---计算星期几
  13. git reset/git checkout./撤销操作
  14. 编写高质量代码改善C#程序的157个建议[泛型集合、选择集合、集合的安全]
  15. 2022年应届大学生做毕设是论文好做点还是设计好,哪个性价比更高
  16. 那些让我印象深刻的bug--04
  17. 功能测试是什么?性能测试是什么?两者有什么区别?
  18. 三子棋 C语言【详解】
  19. NodeJS - 第一个应用程序Hello World
  20. 让你的代码减少90%,这些Java工具库太强大了!

热门文章

  1. 《点燃我,温暖你》爱心代码复现
  2. python中import re_python中re模块
  3. python网易云_Python数据可视化:网易云音乐歌单
  4. Android开发 Studio4.0 APP logo 适配
  5. es数据更新时间_京东到家订单中心系统mysql到es的转化之路
  6. 一、Require函数
  7. Linux性能测试工具之Disk(四)
  8. 美国大学计算机科学博士生排名,usnews美国大学研究生计算机科学专业完整排名...
  9. 2.8 STM32_按键扫描_安富莱
  10. mtd-utils交叉编译