java Concurrent包学习笔记(二):CountDownLatch和CyclicBarrier
一、CountDownLatch
CountDownLatch一个线程同步的工具,是的一个或者多个线程等待其他线程操作完成之后再执行。
CountDownLatch通过一个给定的数值count来进行初始化,方法await()一直阻塞直到当前的count到达零为止,count的数值通过countDown()方法来减1,count的数值一旦设定就不能再修改,如果需要进行修改,请考虑使用CyclicBarrier。
大体看了一下源代码,是通过同步队列来作为计数器来进行控制的。同步队列是在CountDownLatch内部实现了一个静态内部类,countDown()通过调用队列来减1。
有两个典型的应用场景:
第一种是一个开始的信号,所有的task任务等待这个信号。类似于百米赛跑中的信号员,所有的运动员都做好准备,等待信号,信号一来,那就开始运行,裁判等到所有的人员到终点后才能知道结果。
第二种是将一个任务分支N部分由M个线程来处理,等待所有的线程M完成任务后做其他的事情,还是刚才的例子,所有运动员跑完之后,才能知道所有人员的排名情况。
public class CountDownLatchTest {
public static void main(String[] args) throws Exception{
CountDownLatch s = new CountDownLatch(1);
CountDownLatch e = new CountDownLatch(6);
for(int i=0;i<6;i++){
new Thread(new Worker(s,e)).start();
}
System.out.println("i am the judge ,now ,i start the singal");
s.countDown();
System.out.println("waiting all task over"+e.getCount());
e.await();
System.out.println("all is over");
}
}
class Worker implements Runnable{
private final CountDownLatch startSingal ;
private final CountDownLatch endSingal;
public Worker(CountDownLatch startSingal, CountDownLatch endSingal) {
super();
this.startSingal = startSingal;
this.endSingal = endSingal;
}
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"waiting the start singal...."+startSingal.getCount());
//等待开始信号信号
startSingal.await();
System.out.println(Thread.currentThread().getName()+"start to executer");
//结束的计数器减一
endSingal.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
执行结果:
Thread-0waiting the start singal....1
Thread-2waiting the start singal....1
Thread-1waiting the start singal....1
i am the judge ,now ,i start the singal
waiting all task over6
Thread-1start to executer
Thread-3waiting the start singal....1
Thread-4waiting the start singal....1
Thread-3start to executer
Thread-5waiting the start singal....0
Thread-5start to executer
Thread-2start to executer
Thread-0start to executer
Thread-4start to executer
all is over
public static void main(String[] args) throws Exception{
CountDownLatch latch = new CountDownLatch(6);
Executor e = Executors.newFixedThreadPool(6);
System.out.println("thread number is 6,now start");
for(int i=0;i<6;i++){
e.execute(new Worker(latch,i));
}
System.out.println("waiting all is over ");
latch.await();
System.out.println("all is over");
}
}
class Worker implements Runnable{
private final CountDownLatch number;
private int temp;
public Worker(CountDownLatch number, int temp) {
super();
this.number = number;
this.temp = temp;
}
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
System.out.println(Thread.currentThread().getName()+"runnable - "+temp);
number.countDown();
}
}
结果
thread number is 6,now start
waiting all is over
pool-1-thread-4runnable - 3
pool-1-thread-5runnable - 4
pool-1-thread-1runnable - 0
pool-1-thread-3runnable - 2
pool-1-thread-2runnable - 1
pool-1-thread-6runnable - 5
all is over
二、CyclicBarrier
CyclicBarrier是一个所有线程必须等待的一个栅栏,直到指定数量的线程都到达栅栏位置,然后所有线程才可以继续做其他事情。
CyclicBarrier默认的构造方法是CyclicBarrier(int parties),其参数表示屏障拦截的线程数量,每个线程调用await方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。
CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parties, Runnable barrierAction),用于在线程到达屏障时,优先执行barrierAction这个Runnable对象,方便处理更复杂的业务场景。
使用场景一:所有人等待一个信号才能继续做自己的事,这里这个信号就是“所有人到齐”,只有所有人到齐后才能开始“吃饭”
package cyclicbarrier.demo; import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* @author boshen
* @date 2018/12/20
*/
public class CyclicBarrierTest1 {
private CyclicBarrier cb = new CyclicBarrier();
class StudentThread implements Runnable{
private String name;
private int waitSecond;
StudentThread(String name,int waitSecond){
this.name = name;
this.waitSecond = waitSecond;
}
public void run(){
try {
Thread.sleep(waitSecond);
System.out.println("学生:" + name + " 开始等待所有人到齐");
cb.await();
System.out.println("学生:" + name + " 开始吃饭");
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
}
}
} public static void main(String[] args){
CyclicBarrierTest1 cb = new CyclicBarrierTest1();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(cb.new StudentThread("张三",));
executorService.submit(cb.new StudentThread("李四",));
executorService.submit(cb.new StudentThread("王五",));
executorService.submit(cb.new StudentThread("马六",));
executorService.submit(cb.new StudentThread("赵七",));
executorService.shutdown();
}
}
执行结果:
学生:李四 开始等待所有人到齐
学生:马六 开始等待所有人到齐
学生:张三 开始等待所有人到齐
学生:王五 开始等待所有人到齐
学生:赵七 开始等待所有人到齐
学生:赵七 开始吃饭
学生:李四 开始吃饭
学生:马六 开始吃饭
学生:张三 开始吃饭
学生:王五 开始吃饭
使用场景二:所有人等待一个信号才能继续做自己的事,这里这个信号就是“所有人到齐后,老师发话可以吃饭了”,所有人才开始“吃饭”
将上面的例子改造一下,使用了CyclicBarrier(int parties, Runnable barrierAction),即所有人都到齐后先执行barrierAction,然后各线程才继续执行“
package cyclicbarrier.demo; import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* @author boshen
* @date 2018/12/20
*/
public class CyclicBarrierTest2 {
private CyclicBarrier cb = new CyclicBarrier(5, new Runnable() {
public void run() {
System.out.println("老师说: 大家开始吃饭了");
}
});
class StudentThread implements Runnable{
private String name;
private int waitSecond;
StudentThread(String name,int waitSecond){
this.name = name;
this.waitSecond = waitSecond;
}
public void run(){
try {
Thread.sleep(waitSecond);
System.out.println("学生:" + name + " 开始等待所有人到齐");
cb.await();
System.out.println("学生:" + name + " 开始吃饭");
} catch (InterruptedException e) {
} catch (BrokenBarrierException e) {
}
}
} public static void main(String[] args){
CyclicBarrierTest2 cb = new CyclicBarrierTest2();
ExecutorService executorService = Executors.newCachedThreadPool();
executorService.submit(cb.new StudentThread("张三",3));
executorService.submit(cb.new StudentThread("李四",1));
executorService.submit(cb.new StudentThread("王五",4));
executorService.submit(cb.new StudentThread("马六",2));
executorService.submit(cb.new StudentThread("赵七",5));
executorService.shutdown();
}
}
输出:
学生:李四 开始等待所有人到齐
学生:王五 开始等待所有人到齐
学生:张三 开始等待所有人到齐
学生:马六 开始等待所有人到齐
学生:赵七 开始等待所有人到齐
老师说: 大家开始吃饭了
学生:赵七 开始吃饭
学生:李四 开始吃饭
学生:王五 开始吃饭
学生:张三 开始吃饭
学生:马六 开始吃饭
CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;
CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行。
对于CountDownLatch来说,重点是“一个线程(多个线程)等待”,而其他的N个线程在完成“某件事情”之后,可以终止,也可以等待。而对于CyclicBarrier,重点是多个线程,在任意一个线程没有完成,所有的线程都必须等待。
CountDownLatch是计数器,线程完成一个记录一个,只不过计数不是递增而是递减,而CyclicBarrier更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行。
java Concurrent包学习笔记(二):CountDownLatch和CyclicBarrier的更多相关文章
- java Concurrent包学习笔记(一):ExecutorService
一.介绍 ExecutorService是java.util.concurrent包中的一个线程池实现接口.其有两个实现类: 1)ThreadPoolExecutor:普通线程池通过配置线程池大小,能 ...
- java Concurrent包学习笔记(四):BlockingQueue
一.BlockingQueue概述 1.阻塞的含义 BlockingQueue即阻塞队列,从阻塞这个词可以看出,在某些情况下对阻塞队列的访问可能会造成阻塞.被阻塞的情况主要有如下两种: ,当一个线程对 ...
- java Concurrent包学习笔记(六):Exchanger
一.概述 Exchanger 是一个用于线程间协作的工具类,Exchanger用于进行线程间的数据交换,它提供一个同步点,在这个同步点,两个线程可以交换彼此的数据.这两个线程通过exchange 方法 ...
- java Concurrent包学习笔记(五):Semaphore
一.Semaphore 是什么 信号量Semaphore是一个并发工具类,用来控制可同时并发的线程数,其内部维护了一组虚拟许可,构造函数初始化的时候可以指定许可的总数量 每次线程执行操作时先通过ac ...
- java Concurrent包学习笔记(三):ReentrantLock
一.可重入性的理解 从名字上理解,ReenTrantLock的字面意思就是再进入的锁,其实synchronized关键字所使用的锁也是可重入的,两者关于这个的区别不大.两者都是同一个线程每进入一次,锁 ...
- java Concurrent包学习笔记(七):ConcurrentHashMap
(注意:以下讲解的ConcurrentHashMap是jdk 1.8的) 一.ConcurrentHashMap的数据结构 ConcurrentHashMap在1.8中的实现,相比于1.7的版本基本上 ...
- java之jvm学习笔记二(类装载器的体系结构)
java的class只在需要的时候才内转载入内存,并由java虚拟机的执行引擎来执行,而执行引擎从总的来说主要的执行方式分为四种, 第一种,一次性解释代码,也就是当字节码转载到内存后,每次需要都会重新 ...
- java.util.concurrent包学习笔记(一)Executor框架
类图: 其实从类图我们能发现concurrent包(除去java.util.concurrent.atomic 和 java.util.concurrent.locks)中的内容并没有特别多,大概分为 ...
- java package 包 学习笔记
编译命令示例: javac -d . Main.java 注:带参数-d自动建立文件目录, 只使用javac 则需要手工创建目录 把 class文件打包 jar命令 jar cvf T.jar *; ...
随机推荐
- (转)python virtual_env 的使用 + 将原来的虚拟环境部署到新环境
原文链接: https://blog.csdn.net/poxiaonie/article/details/78820015
- (转)Docker volume plugin - enabled create local volume on docker host
原文地址:https://hub.docker.com/r/cwspear/docker-local-persist-volume-plugin/ Short Description Create n ...
- C语言实现Base64编码/解码
Bse64是一种以64个可打印字符对二进制数据进行编码的编码算法.base64在对数据进行编码时以三个8位字符型数据为一组,取这三个字符型数据的ASCII码,然后以6位为一组组成4个新的数据,这4个新 ...
- Java复习——I/O与序列化
File类 java.io.File只用于表示文件(目录)的信息(名称.大小等),不能用于文件内容的访问,我们可以通过通过给其构造函数传一个路径来构建以文件,传入的路径名有一个小问题,就是Window ...
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(1)-前言与目录(转)
开发工具:VS2015(2012以上)+SQL2008R2以上数据库 您可以有偿获取一份最新源码联系QQ:729994997 价格 666RMB 升级后界面效果如下: 日程管理 http://ww ...
- CocoStudio资源区导入Plist/PSD文件
这两种文件在使用中和普通文件稍有不同,下作简单介绍.如有不适的地方欢迎批评指正. 首先简单说一下Plist文件,Plist文件通常用于储存用户设置,也可以用于存储捆绑的信息,该功能在旧式的Mac OS ...
- DOM库及常用方法封装
节点 nodeType nodeName nodeValue 元素节点 1 大写的标签名 null 文本节点 3 #text 文本内容 注释节点 8 #comment 注释内容 document 9 ...
- 跟我一起学kafka(一)
从昨天下午接到新任务,要采集一个法院网站得所有公告,大概是需要采集这个网站得所有公告列表里得所有txt内容,txt文件里边是一件件赤裸裸得案件,记录这案由,原告被告等相关属性(不知道该叫什么就称之为属 ...
- [转] c# 的传递参数值传递与传递引用的区别,ref与out区别
值传递 C#默认都是值传递的,就是复制变量的一个副本传递给方法,所以在退出方法后,对变量的修改无效. 但是要注意,当传递是引用类型时,因为引用类型是一个引用的地址,所以修改引用地址指向的对象时,一样会 ...
- Jquery.Ajax的使用方法
1.Get $('.manager_republish.notVIP').click(function () { $.ajax({ async: false, type: "get" ...