JUC并发工具包之CyclicBarrier
1、简介
CyclicBarrier是一个同步器,允许多个线程等待彼此直到达一个执行点(barrier)。
CyclicBarrier都是在多个线程必须等到彼此都到达同一个执行点后才执行一段逻辑时才被使用。
barrier被叫做cyclic是因为阻塞线程恢复后可以重复使用barrier
2、使用
CyclicBarrier的构造器很简单,传入一个整形表示线程数再调用barrier实例的await()方法表示所有线程要到达这一共同的执行点。
public CyclicBarrier(int parties)
所有需要同步执行的线程也被叫做parties,调用await()方法可以注册一个线程到达过执行点(barrier point)。
这个调用是同步的并且当前线程会挂起直至其他线程都调用到barrier的await()方法,该场景中既定数量的线程都调用了await()方法叫做tripping the barrier(源码中这种描述出现多次)
或者,我们也可以传第二个参数给构造方法,这是一个Runnable的实例,这将作为最后一个线程trips the barrier。
public CyclicBarrier(int parties, Runnable barrierAction)
3、实现
为了实际使用一下CyclicBarrier,我们来考虑下面一个场景:
固定数量的线程执行一段操作并把对应的结果存到列表中,当所有的线程执行完操作,其中一个(也就是最后一个trips the barrier的线程)开始处理其他线程获取到的结果。
我们开始实现一下这个类:
public class CyclicBarrierDemo {
private CyclicBarrier cyclicBarrier;
private List<List<Integer>> partialResults
= Collections.synchronizedList(new ArrayList<>());
private Random random = new Random();
private int NUM_PARTIAL_RESULTS;
private int NUM_WORKERS;
// ...
}
这个类已经很直接了当了,NUM_WORKERS是线程数,NUM_PARTIAL_RESULTS是每个线程即将要输出结果的数量
最后,我们定义一个partialResults来存储所有线程存储的结果,请注意这个列表是一个SynchronizedList,因为多个线程会同时向它写入数据(普通ArrayList的add方法不是线程安全的)
现在我们实现一下每个工作线程的逻辑:
public class CyclicBarrierDemo {
// ...
class NumberCruncherThread implements Runnable {
@Override
public void run() {
String thisThreadName = Thread.currentThread().getName();
List<Integer> partialResult = new ArrayList<>();
// Crunch some numbers and store the partial result
for (int i = 0; i < NUM_PARTIAL_RESULTS; i++) {
Integer num = random.nextInt(10);
System.out.println(thisThreadName
+ ": Crunching some numbers! Final result - " + num);
partialResult.add(num);
}
partialResults.add(partialResult);
try {
System.out.println(thisThreadName
+ " waiting for others to reach barrier.");
cyclicBarrier.await();
} catch (InterruptedException e) {
// ...
} catch (BrokenBarrierException e) {
// ...
}
}
}
}
我们现在实现所有现在都到执行点后的逻辑,为了把逻辑写简单点,我们就把partialResults列表中的值全部相加。
public class CyclicBarrierDemo {
// ...
class AggregatorThread implements Runnable {
@Override
public void run() {
String thisThreadName = Thread.currentThread().getName();
System.out.println(
thisThreadName + ": Computing sum of " + NUM_WORKERS
+ " workers, having " + NUM_PARTIAL_RESULTS + " results each.");
int sum = 0;
for (List<Integer> threadResult : partialResults) {
System.out.print("Adding ");
for (Integer partialResult : threadResult) {
System.out.print(partialResult+" ");
sum += partialResult;
}
System.out.println();
}
System.out.println(thisThreadName + ": Final result = " + sum);
}
}
}
那现在最后一步就是构建CyclicBarrier并把所有的逻辑在main()方法中跑通:
public class CyclicBarrierDemo {
// Previous code
public void runSimulation(int numWorkers, int numberOfPartialResults) {
NUM_PARTIAL_RESULTS = numberOfPartialResults;
NUM_WORKERS = numWorkers;
cyclicBarrier = new CyclicBarrier(NUM_WORKERS, new AggregatorThread());
System.out.println("Spawning " + NUM_WORKERS
+ " worker threads to compute "
+ NUM_PARTIAL_RESULTS + " partial results each");
for (int i = 0; i < NUM_WORKERS; i++) {
Thread worker = new Thread(new NumberCruncherThread());
worker.setName("Thread " + i);
worker.start();
}
}
public static void main(String[] args) {
CyclicBarrierDemo demo = new CyclicBarrierDemo();
demo.runSimulation(5, 3);
}
}
以上代码中,我们在CyclicBarrier中初始化了5个线程,每个线程会输出3个整形并存储到partialResults中。
4、输出结果
以下就是上述程序运行一次的输出结果,每次执行可能都会有不同的结果因为线程会以不同的顺序执行。
Spawning 5 worker threads to compute 3 partial results each
Thread 0: Crunching some numbers! Final result - 6
Thread 0: Crunching some numbers! Final result - 2
Thread 0: Crunching some numbers! Final result - 2
Thread 0 waiting for others to reach barrier.
Thread 1: Crunching some numbers! Final result - 2
Thread 1: Crunching some numbers! Final result - 0
Thread 1: Crunching some numbers! Final result - 5
Thread 1 waiting for others to reach barrier.
Thread 3: Crunching some numbers! Final result - 6
Thread 3: Crunching some numbers! Final result - 4
Thread 3: Crunching some numbers! Final result - 0
Thread 3 waiting for others to reach barrier.
Thread 2: Crunching some numbers! Final result - 1
Thread 2: Crunching some numbers! Final result - 1
Thread 2: Crunching some numbers! Final result - 0
Thread 2 waiting for others to reach barrier.
Thread 4: Crunching some numbers! Final result - 9
Thread 4: Crunching some numbers! Final result - 3
Thread 4: Crunching some numbers! Final result - 5
Thread 4 waiting for others to reach barrier.
Thread 4: Computing final sum of 5 workers, having 3 results each.
Adding 6 2 2
Adding 2 0 5
Adding 6 4 0
Adding 1 1 0
Adding 9 3 5
Thread 4: Final result = 46
5、总结
- 能看到CyclicBarrier的适用场景
- 实现了一段逻辑:固定线程都到达一个执行点后再执行其他逻辑
最后这些例子的实现都可以在Github上找到
6、思考
前文说过
barrier被叫做cyclic是因为阻塞线程恢复后可以重复使用barrier
那这里的重复使用怎么理解呢?
- 可以看下这篇博客:阿里面试居然跟我扯了半小时的CyclicBarrier
最后可以看下王者荣耀的例子,十分帮忙理解CyclicBarrier。
JUC并发工具包之CyclicBarrier的更多相关文章
- JUC并发工具包之CyclicBarrier & CountDownLatch的异同
1.介绍 本文我们将比较一下CyclicBarrier和CountDownLatch并了解两者的相似与不同. 2.两者是什么 当谈到并发,将这两者概念化的去解释两者是做什么的,这其实是一件很有挑战的事 ...
- JUC并发工具包之Semaphore
目录 Semaphore (JDK) Timed Semaphore (Apache Commons) Semaphore vs. Mutex CodeRepo Semaphore (JDK) 我们使 ...
- JUC并发工具包之CountDownLatch
1.介绍 本文将介绍CountDownLatch并给出实践中的几个例子,通过使用CountDownLatch我们可以让一个线程阻塞直到其他一个或多个线程执行完成. A synchronization ...
- Java 并发工具包 java.util.concurrent 用户指南
1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...
- Java并发编程-并发工具包(java.util.concurrent)使用指南(全)
1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...
- Java 8并发工具包漫游指南
Java 8并发工具包简介 Java 8并发工具包由3个包组成,分别是java.util.concurrent.java.util.concurrent.atomic和java.util.concur ...
- Java_并发工具包 java.util.concurrent 用户指南(转)
译序 本指南根据 Jakob Jenkov 最新博客翻译,请随时关注博客更新:http://tutorials.jenkov.com/java-util-concurrent/index.html.本 ...
- Java 并发工具包 java.util.concurrent 大全
1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...
- 1. java.util.concurrent - Java 并发工具包
1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...
随机推荐
- request-promise post请求微信小程序云函数调用http
微信小程序不支持http调用,但是可以通过服务器或者云函数实现,云函数相当于云服务器中的一段代码,可以使用http协议 首先要云函数安装request-promise,右键云函数,在终端中打开,输入n ...
- 在VirtualBox中调整Raspbian分辨率
参考自一路阳光随行发表的virtualBox设置虚拟机分辨率大小中ubuntu虚拟机分辨率的设置方法. 启动Raspbian虚拟机,点击 窗口主菜单里的设备->安装增强功能.系统后会自动挂载增强 ...
- HP UNIX 查看 修改IP地址
# 查看所有IP地址h01_root[/]# lanscan Hardware Station Crd Hdw Net-Interface NM MAC HP-DLPI DLPI Path Addre ...
- 第4章 Function语意学
第4章 Function语意学 目录 第4章 Function语意学 4.1 Member的各种调用方式 Nonstatic Member Function(非静态成员函数) virtual Memb ...
- 关于C语言中的位运算符的学习即应用
一.运算符的种类和运算规则: & 与:全1则1,有0则0 | 或:有1则1,全0则0 ^ 异或:相同为0,不同为1 ~ 取反:~是一元运算符,用来对一 ...
- week01-绪论作业
一.有理数的抽象数据模型 ADT Rational { 数据对象: D={e1,e2|e1,e2属于ElemType类型}//ElemType为自定义的整数集合类型 数据关系: R={<e1,e ...
- css 兼容性总结
1.就是ie6双倍边距的问题,在使用了float的情况下,不管是向左还是向右都会出现双倍,最简单的解决方法就是用display:inline;加到css里面去. 2.文字本身的大小不兼容.同样是fon ...
- NoSQL数据库的四大分类的分析
分类 Examples举例 典型应用场景 数据模型 优点 缺点 键值(key-value) Tokyo Cabinet/Tyrant, Redis, Voldemort, Oracle BDB 内容缓 ...
- c#视频位置
static void Main(string[] args) { string scoure = @"C:\Documents and Settings\Administra ...
- Redis快速入门教程
1.Redis介绍 Redis说白了就是个存放Key-Value数据接口的内存存储系统,主要用作数据库缓存和消息代理. 内部支持sring,hash,list,set,sorted-set五种数据结构 ...