CountDownLatch、CyclicBarrier、Semaphore的区别,你知道吗?
点击上方 好好学java ,选择 星标 公众号
重磅资讯、干货,第一时间送达
今日推荐:是时候扔掉Postman了,又一个被低估的IDEA插件出来了...个人原创+1博客:点击前往,查看更多
链接:https://segmentfault.com/a/1190000012234469
CountDownLatch
CountDownLatch 类位于 java.util.concurrent 包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。
CountDownLatch类只提供了一个构造器:
public CountDownLatch(int count) { }; //参数count为计数值
然后下面这3个方法是CountDownLatch类中最重要的方法:
public void await() throws InterruptedException { }; //调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; //和await()类似,只不过等待一定的时间后count值还没变为0的话就会继续执行
public void countDown() { }; //将count值减1
代码实现
package sychronized;import static net.mindview.util.Print.*;
import java.util.concurrent.*;class Task implements Runnable{private static int count = 0;private final int id = count++;final CountDownLatch latch ;public Task(CountDownLatch latch){this.latch = latch;}@Overridepublic void run(){try {print(this+"正在执行");TimeUnit.MILLISECONDS.sleep(3000);print(this+"执行完毕");latch.countDown();} catch (InterruptedException e) {print(this + " 被中断");}}@Overridepublic String toString() {return "Task-"+id;}
}public class Test {public static void main(String[] args) {final CountDownLatch latch = new CountDownLatch(2);ExecutorService exec = Executors.newCachedThreadPool();exec.execute(new Task(latch));exec.execute(new Task(latch));try {print("等待2个子线程执行完毕...");long start = System.currentTimeMillis();latch.await();long end = System.currentTimeMillis();print("2个子线程已经执行完毕 "+(end - start));print("继续执行主线程");}catch (InterruptedException e){print("主线程被中断");}exec.shutdown();}
}#输出结果:
等待2个子线程执行完毕...
Task-0正在执行
Task-1正在执行
Task-0执行完毕
Task-1执行完毕
2个子线程已经执行完毕 3049
继续执行主线程
CyclicBarrier
字面意思回环栅栏,通过它可以实现让一组线程等待至某个状态之后再全部同时执行。叫做回环是因为当所有等待线程都被释放以后,CyclicBarrier可以被重用。我们暂且把这个状态就叫做barrier,当调用await()方法之后,线程就处于barrier了。
CyclicBarrier类位于java.util.concurrent包下,CyclicBarrier提供2个构造器:
参数parties指让多少个线程或者任务等待至barrier状态
参数barrierAction为当这些线程都达到barrier状态时会执行的内容
public CyclicBarrier(int parties, Runnable barrierAction) {}
public CyclicBarrier(int parties) {}
然后CyclicBarrier中最重要的方法就是 await 方法,它有2个重载版本:
第一个版本比较常用,用来挂起当前线程,直至所有线程都到达barrier状态再同时执行后续任务;
第二个版本是让这些线程等待至一定的时间,如果还有线程没有到达barrier状态就直接让到达barrier的线程执行后续任务。
public int await() throws InterruptedException, BrokenBarrierException { };
public int await(long timeout, TimeUnit unit)throws InterruptedException,BrokenBarrierException,TimeoutException { };
代码展示
package sychronized;import java.util.Random;
import java.util.concurrent.*;
import static net.mindview.util.Print.*;class WriteTask implements Runnable{private static int count = 0;private final int id = count++;private CyclicBarrier barrier ;private static Random random = new Random(47);public WriteTask(CyclicBarrier cyclicBarrier) {this.barrier = cyclicBarrier;}@Overridepublic void run() {print(this+"开始写入数据...");try {TimeUnit.MILLISECONDS.sleep(random.nextInt(5000)); //以睡眠来模拟写入数据操作print(this+"写入数据完毕,等待其他线程写入完毕"+" "+System.currentTimeMillis());barrier.await();} catch (InterruptedException e) {print(this + "is interrupted!");}catch(BrokenBarrierException e){throw new RuntimeException(e);}print("所有任务写入完毕,继续处理其他任务... "+System.currentTimeMillis());}@Overridepublic String toString() {return getClass().getSimpleName()+"-"+id;}
}public class CyclicBarrierTest {public static void main(String[] args) {int N = 4;CyclicBarrier barrier = new CyclicBarrier(N);ExecutorService exec = Executors.newCachedThreadPool();for(int i = 0; i < N; ++i){exec.execute(new WriteTask(barrier));}exec.shutdown();}
}#输出结果:
WriteTask-3 开始写入数据...
WriteTask-2 开始写入数据...
WriteTask-1 开始写入数据...
WriteTask-0 开始写入数据...
WriteTask-2 写入数据完毕,等待其他线程写入完毕 1512048648904
WriteTask-1 写入数据完毕,等待其他线程写入完毕 1512048650042
WriteTask-0 写入数据完毕,等待其他线程写入完毕 1512048650209
WriteTask-3 写入数据完毕,等待其他线程写入完毕 1512048652606
所有任务写入完毕,继续处理其他任务... 1512048652607
所有任务写入完毕,继续处理其他任务... 1512048652607
所有任务写入完毕,继续处理其他任务... 1512048652607
所有任务写入完毕,继续处理其他任务... 1512048652607
如果说想在所有线程写入操作完之后,进行额外的其他操作可以为CyclicBarrier提供Runnable参数:
package sychronized;import java.util.Random;
import java.util.concurrent.*;
import static net.mindview.util.Print.*;class WriteTask implements Runnable{private static int count = 0;private final int id = count++;private CyclicBarrier barrier ;private static Random random = new Random(47);public WriteTask(CyclicBarrier cyclicBarrier) {this.barrier = cyclicBarrier;}@Overridepublic void run() {print(this+" 开始写入数据...");try {TimeUnit.MILLISECONDS.sleep(random.nextInt(5000)); //以睡眠来模拟写入数据操作print(this+" 写入数据完毕,等待其他线程写入完毕"+" "+System.currentTimeMillis());barrier.await();TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {print(this + "is interrupted!");}catch(BrokenBarrierException e){throw new RuntimeException(e);}print("所有任务写入完毕,继续处理其他任务... "+System.currentTimeMillis()+Thread.currentThread());}@Overridepublic String toString() {return getClass().getSimpleName()+"-"+id;}
}public class CyclicBarrierTest {public static void main(String[] args) {int N = 4;CyclicBarrier barrier = new CyclicBarrier(N, new Runnable() {@Overridepublic void run() {print(Thread.currentThread());}});ExecutorService exec = Executors.newCachedThreadPool();for(int i = 0; i < N; ++i){exec.execute(new WriteTask(barrier));}exec.shutdown();}
}
#输出结果为:
WriteTask-3 开始写入数据...
WriteTask-1 开始写入数据...
WriteTask-2 开始写入数据...
WriteTask-0 开始写入数据...
WriteTask-1 写入数据完毕,等待其他线程写入完毕 1512049061954
WriteTask-2 写入数据完毕,等待其他线程写入完毕 1512049063092
WriteTask-0 写入数据完毕,等待其他线程写入完毕 1512049063261
WriteTask-3 写入数据完毕,等待其他线程写入完毕 1512049065657
Thread[pool-1-thread-4,5,main]
所有任务写入完毕,继续处理其他任务... 1512049065668Thread[pool-1-thread-2,5,main]
所有任务写入完毕,继续处理其他任务... 1512049065668Thread[pool-1-thread-1,5,main]
所有任务写入完毕,继续处理其他任务... 1512049065668Thread[pool-1-thread-4,5,main]
所有任务写入完毕,继续处理其他任务... 1512049065668Thread[pool-1-thread-3,5,main]
从结果可以看出,当四个线程都到达barrier状态后,会从四个线程中选择一个线程去执行Runnable。
另外CyclicBarrier是可以重用的,看下面这个例子:
package sychronized;import java.util.Random;
import java.util.concurrent.*;
import static net.mindview.util.Print.*;class WriteTask implements Runnable{private static int count = 0;private final int id = count++;private CyclicBarrier barrier ;private static Random random = new Random(47);public WriteTask(CyclicBarrier cyclicBarrier) {this.barrier = cyclicBarrier;}@Overridepublic void run() {while (!Thread.interrupted()){print(this+" 开始写入数据...");try {TimeUnit.MILLISECONDS.sleep(random.nextInt(5000)); //以睡眠来模拟写入数据操作print(this+" 写入数据完毕,等待其他线程写入完毕"+" "+System.currentTimeMillis());barrier.await();TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {print(this + "is interrupted!");}catch(BrokenBarrierException e){throw new RuntimeException(e);}print("所有任务写入完毕,继续处理其他任务... "+System.currentTimeMillis());}}@Overridepublic String toString() {return getClass().getSimpleName()+"-"+id;}
}class CyclicBarrierManager implements Runnable{private CyclicBarrier barrier ;private ExecutorService exec;public CyclicBarrierManager(CyclicBarrier barrier, ExecutorService exec,int N){this.barrier = barrier ;this.exec = exec;for (int i = 0; i < N-1; ++i){exec.execute(new WriteTask(barrier));}}@Overridepublic void run(){while (!Thread.interrupted()){try {barrier.await();}catch (InterruptedException e){print(getClass().getSimpleName()+" 被中断了!");}catch (BrokenBarrierException e){throw new RuntimeException(e);}}}
}public class CyclicBarrierTest {public static void main(String[] args) throws Exception{int N = 4;CyclicBarrier barrier = new CyclicBarrier(N);ExecutorService exec = Executors.newCachedThreadPool();exec.execute(new CyclicBarrierManager(barrier,exec,N));exec.shutdown();}
}#输出结果:
WriteTask-1 开始写入数据...
WriteTask-2 开始写入数据...
WriteTask-0 开始写入数据...
WriteTask-2 写入数据完毕,等待其他线程写入完毕 1512051484365
WriteTask-0 写入数据完毕,等待其他线程写入完毕 1512051485503
WriteTask-1 写入数据完毕,等待其他线程写入完毕 1512051488068
所有任务写入完毕,继续处理其他任务... 1512051488078
所有任务写入完毕,继续处理其他任务... 1512051488078
WriteTask-2 开始写入数据...
所有任务写入完毕,继续处理其他任务... 1512051488078
WriteTask-1 开始写入数据...
WriteTask-0 开始写入数据...
WriteTask-0 写入数据完毕,等待其他线程写入完毕 1512051488513
WriteTask-1 写入数据完毕,等待其他线程写入完毕 1512051489045
WriteTask-2 写入数据完毕,等待其他线程写入完毕 1512051489945
所有任务写入完毕,继续处理其他任务... 1512051489955
WriteTask-0 开始写入数据...
所有任务写入完毕,继续处理其他任务... 1512051489955
所有任务写入完毕,继续处理其他任务... 1512051489955
WriteTask-2 开始写入数据...
WriteTask-1 开始写入数据...
WriteTask-2 写入数据完毕,等待其他线程写入完毕 1512051490155
WriteTask-1 写入数据完毕,等待其他线程写入完毕 1512051494477
WriteTask-0 写入数据完毕,等待其他线程写入完毕 1512051494823
所有任务写入完毕,继续处理其他任务... 1512051494833
所有任务写入完毕,继续处理其他任务... 1512051494833
WriteTask-0 开始写入数据...
所有任务写入完毕,继续处理其他任务... 1512051494833
WriteTask-1 开始写入数据...
WriteTask-2 开始写入数据...
WriteTask-2 写入数据完毕,等待其他线程写入完毕 1512051494961
WriteTask-0 写入数据完毕,等待其他线程写入完毕 1512051496040
WriteTask-1 写入数据完毕,等待其他线程写入完毕 1512051498121
所有任务写入完毕,继续处理其他任务... 1512051498132
所有任务写入完毕,继续处理其他任务... 1512051498132
WriteTask-1 开始写入数据...
所有任务写入完毕,继续处理其他任务... 1512051498132
Semaphore
Semaphore翻译成字面意思为 信号量,Semaphore 可以同时让多个线程同时访问共享资源,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。
Semaphore类位于java.util.concurrent包下,它提供了2个构造器:
public Semaphore(int permits) { //参数permits表示许可数目,即同时可以允许多少线程进行访问sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) { //这个多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可sync = (fair)? new FairSync(permits) : new NonfairSync(permits);
}
下面说一下Semaphore类中比较重要的几个方法,首先是acquire()、release()方法:
public void acquire() throws InterruptedException { } //获取一个许可
public void acquire(int permits) throws InterruptedException { } //获取permits个许可
public void release() {} //释放一个许可
public void release(int permits) {} //释放permits个许可
acquire()用来获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。
release()用来释放许可。
注意,在释放许可之前,必须先获获得许可。
这4个方法都会被阻塞,如果想立即得到执行结果,可以使用下面几个方法:
public boolean tryAcquire() { }; //尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
public boolean tryAcquire(int permits) { }; //尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false
public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { }; //尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false
package sychronized;
import java.util.Random;
import java.util.concurrent.*;
import static net.mindview.util.Print.*;class Worker implements Runnable{private static int count = 0;private final int id = count++;private int finished = 0;private Random random = new Random(47);private Semaphore semaphore;public Worker(Semaphore semaphore){this.semaphore = semaphore;}@Override public void run(){try {while (!Thread.interrupted()){semaphore.acquire();print(this+" 占用一个机器在生产... ");TimeUnit.MILLISECONDS.sleep(random.nextInt(2000));synchronized (this){print(" 已经生产了"+(++finished)+"个产品,"+"释放出机器");}semaphore.release();}} catch (InterruptedException e) {e.printStackTrace();}}@Overridepublic String toString() {return getClass().getSimpleName()+"-"+id;}
}public class SemaphoreTest {public static void main(String[] args) {int N = 8; //工人数Semaphore semaphore = new Semaphore(5); //机器数目ExecutorService exec = Executors.newCachedThreadPool();for (int i = 0; i < N; ++i){exec.execute(new Worker(semaphore));}exec.shutdown();}
}
总结
CountDownLatch和CyclicBarrier都能够实现线程之间的等待,只不过它们侧重点不同:
CountDownLatch 一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;
CyclicBarrier 一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;
CountDownLatch 是不能够重用的,而 CyclicBarrier 是可以重用的。
Semaphore 其实和锁有点类似,它一般用于控制对 某组 资源的访问权限,而锁是控制对 某个 资源的访问权限。
CountDownLatch、CyclicBarrier、Semaphore的区别,你知道吗?相关推荐
- (面经总结)一篇文章带你完整复习 Java 中并发关键字(CountDownLatch/CyclicBarrier/Semaphore/Volatile)
文章目录 一.倒计数器:CountDownLatch 二.循环栅栏:CyclicBarrier 三.信号量:Semaphore 四.volatile 关键字的作用 一.倒计数器:CountDownLa ...
- JUC介绍--常用辅助类(CountDownLatch CyclicBarrier Semaphore)
CountDownLatch减法计数器 每次有线程调用countDownLatch.countDown()数量就-1 数量减到0时,countDownLatch.await()会被唤醒,继续向下执行 ...
- CountDownLatch CyclicBarrier Semaphore
CountDownLacth CountDownLacth(倒计数锁存器)到底有什么用呢?我们来看下面这个场景. 我们又有小片片要开始拍摄了,这个小片片需要5个演员来演,开演之前,导演需要这5个演员全 ...
- CountDownLatch和Semaphore使用场景
CountDownLatch CountDownLatch位于java.util.concurrent包下,利用它可以实现类似计数器的功能.比如有一个任务A,它要等到其它3任务完成才能执行,此时就可以 ...
- 【嵌入式操作系统】FreeRTOS信号量mutex和semaphore的区别
今天学习信号量mutex和semaphore的区别,找到了正点原子的博客介绍,讲的挺详细的.建议大家阅读 转载自:https://blog.csdn.net/nippon1218/article/de ...
- Java并发工具类:CountDownLatch、Semaphore、CyclicBarrier、Exchanger、Phaser
本文目录: 1.CountDownLatch(闭锁) 1.CountDownLatch 例子 2.CyclicBarrier(循环栅栏) 1.CyclicBarrier 例子 2.CountDownL ...
- 《Java 7 并发编程指南》学习概要 (3)Semaphore, CountDownLatch, CyclicBarrier , Phaser, Exchanger...
1.Semaphore 信号量 Semaphore(信号量)是一个控制访问多个共享资源的计数器. 当一个线程想要访问某个共享资源,首先,它必须获得semaphore.如果semaphore的内部计数 ...
- Countdownlatch、CyclicBarrier、join区别
1.join 模拟现在有三个worker,采用join的方法控制: package org.pbccrc.org.pbccrc.thread;public class Test {public sta ...
- CyclicBarrier和CountDownLatch的用法与区别
前言 CyclicBarrier和CountDownLatch这两个工具都是在java.util.concurrent包下,并且平时很多场景都会使用到. 本文将会对两者进行分析,记录他们的用法和区别. ...
最新文章
- 推荐一位玩自动化的 Python 爱好者
- 为了用户体验,不要做浏览器兼容
- 六、MySql索引分类
- UNICODE转多字节
- python PyQt5 QComboBox类(下拉列表框、组合下拉框)
- 【excel】日期函数DateDif
- 大佬对协程以及try except的详细解释
- Entity Framework Code First模式基础知识及入门实例01
- 小技巧 ----- 关于Java中的System.arraycopy()
- 恒生电子offer含金量_收获8个Offer,来给大家分享备战和面试经验
- 医疗管理系统HIS源码
- 分集与复用,分集用于抵抗信道衰落,复用用于提升系统容量
- 用react制作半圆形进度条
- ios label内字体置顶_IOS_Vertically align UILabel文本置顶 | 学步园
- elementui的表单验证踩坑-动态绑定输入框required后只显示英文提示
- 福特汉姆计算机专业,福特汉姆大学计算机如何
- 您的美团账户,美团互助未经客户同意自动扣费0.01元是什么情况
- SQL练习题_ 查询每个部门工资最高的前两名的姓名和部门名称【多测师_何sir】
- 美国top10计算机phd申请案例,申请美国计算机PHD全奖应该怎么做
- 笔记本连接老显示器一直弹出‘输出信号超出范围“终于解决了--通过删除显示器的注册表
热门文章
- (转)几种流行的JS框架的选择
- oracle adg的特点是什么,Oracle12c ADG新特性
- 区块链BaaS云服务(29) 溪塔科技 CITA-Cloud 二
- 基于区块链的健康链系统设计与实现(6)结束语
- 初等数论--同余方程--同余方程组:中国剩余定理
- linux 中ans 用法,JSON简介以及用法汇总
- 基于异或,取反和循环移位实现一个简单的加密解密函数
- 【Win32汇编】__declspec(naked)裸函数
- androidstuido_schooltest_6_media_service
- angr学习笔记(5)(栈符号化)