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上找到

原文地址:https://www.baeldung.com/java-cyclic-barrier

6、思考

前文说过

barrier被叫做cyclic是因为阻塞线程恢复后可以重复使用barrier

那这里的重复使用怎么理解呢?

最后可以看下王者荣耀的例子,十分帮忙理解CyclicBarrier。

JUC并发工具包之CyclicBarrier的更多相关文章

  1. JUC并发工具包之CyclicBarrier & CountDownLatch的异同

    1.介绍 本文我们将比较一下CyclicBarrier和CountDownLatch并了解两者的相似与不同. 2.两者是什么 当谈到并发,将这两者概念化的去解释两者是做什么的,这其实是一件很有挑战的事 ...

  2. JUC并发工具包之Semaphore

    目录 Semaphore (JDK) Timed Semaphore (Apache Commons) Semaphore vs. Mutex CodeRepo Semaphore (JDK) 我们使 ...

  3. JUC并发工具包之CountDownLatch

    1.介绍 本文将介绍CountDownLatch并给出实践中的几个例子,通过使用CountDownLatch我们可以让一个线程阻塞直到其他一个或多个线程执行完成. A synchronization ...

  4. Java 并发工具包 java.util.concurrent 用户指南

    1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...

  5. Java并发编程-并发工具包(java.util.concurrent)使用指南(全)

    1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...

  6. Java 8并发工具包漫游指南

    Java 8并发工具包简介 Java 8并发工具包由3个包组成,分别是java.util.concurrent.java.util.concurrent.atomic和java.util.concur ...

  7. Java_并发工具包 java.util.concurrent 用户指南(转)

    译序 本指南根据 Jakob Jenkov 最新博客翻译,请随时关注博客更新:http://tutorials.jenkov.com/java-util-concurrent/index.html.本 ...

  8. Java 并发工具包 java.util.concurrent 大全

    1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...

  9. 1. java.util.concurrent - Java 并发工具包

    1. java.util.concurrent - Java 并发工具包 Java 5 添加了一个新的包到 Java 平台,java.util.concurrent 包.这个包包含有一系列能够让 Ja ...

随机推荐

  1. SpringBoot+JPA+SpringSeurity+JWT

    目的:使用这个框架主要就是为了解决高并发环境下登陆操作对数据库及服务器的压力,同时能保证安全性: 加载时,SpringSecurity定义拦截器和添加两个Fitler: 登陆时,登陆成功,通过传入的信 ...

  2. 一起学Vue:UI框架(element-ui)

    目标 使用Vue+ElementUI构建一个非常简单CRUD应用程序,以便您更好地了解它的工作方式. 效果页面 比如我们要实现这样列表.新增.编辑三个页面: 列表页面 新增页面 编辑页面 安装elem ...

  3. 在国内使用Google验证码reCaptcha

    如今各大网站都不可缺少的一部分就是验证码,验证码具有防止恶意批量操作,保护账户安全等作用.但是现在各种暴力破解验证码的手段层出不穷,验证码的保护也就失去了意义.所以各大平台为了应对这种情况也是使用类似 ...

  4. POI Excel 导入导出重点

    HSSF是指2007年以前的,XSSF是指2007年版本以上的 这个还是比较好用的,这些总结来自Apache的官方向导的点点滴滴 详细的请参考http://poi.apache.org/spreads ...

  5. Java学习的第四十四天

    1.例5.4将二维数组的行列互换 public class cjava { public static void main(String []args) { int [][]a=new int [][ ...

  6. Nginx跳转配置

    1.携带目录调转到后端,后端无目录,location配置如下 例子: i2.jusdacfj.com/ideas_edi/--10.0.2.137:7040/10.0.2.138:7040 locat ...

  7. 使用AI技术获取图片文字与识别图像内容

    获取图片文字 如何使用python获取图片文字呢? 关注公众号[轻松学编程]了解更多- 1.通过python的第三方库pytesseract获取 通过pip install pytesseract导入 ...

  8. SpringBoot第四集:整合JdbcTemplate和JPA(2020最新最易懂)

    SpringBoot第四集:整合JdbcTemplate和JPA(2020最新最易懂) 当前环境说明: Windows10_64 Maven3.x JDK1.8 MySQL5.6 SpringTool ...

  9. Python3网络学习案例一:Ping详解

    1. 使用Ping做什么 ping用于确定本地主机是否能与另一台主机成功交换(发送与接收)数据包,再根据返回的信息,就可以推断TCP/IP参数是否设置正确,以及运行是否正常.网络是否通畅等. 2. 效 ...

  10. CSS换行和省略号

    换行 原地址:https://www.cnblogs.com/meowcool/p/10130103.html //强制不换行 div{ white-space:nowrap; } //自动换行 di ...