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. 02 . Go框架之Gin框架从入门到熟悉(数据解析和绑定,渲染,重定向,同步异步,中间件)

    数据解析和绑定 json数据解析和绑定 package main import ( "github.com/gin-gonic/gin" "net/http" ...

  2. java处理大数据量任务时的可用思路--未验证版,具体实现方法有待实践

    1.Bloom filter适用范围:可以用来实现数据字典,进行数据的判重,或者集合求交集基本原理及要点:对于原理来说很简单,位数组+k个独立hash函数.将hash函数对应的值的位数组置1,查找时如 ...

  3. K8S使用群晖DS218+的NFS

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  4. shell脚本之整数二次元比较操作符

    1.常用二次元比较操作符知识 我们也可以通过man test查看 提示: (1) ">"和"<"符号,在单括中需要转义,在双中括号中不需要转义,因为 ...

  5. 热部署只知道devtools吗?JRebel不香吗?

    持续原创输出,点击上方蓝字关注我 目录 前言 JRebel收费怎么破? 什么是本地热部署? 什么是远程热部署? JRebel和devtools的区别 如何安装JRebel? 如何本地热部署? 如何远程 ...

  6. 【QT】 QThread部分源码浅析

    本文章挑出QThread源码中部分重点代码来说明QThread启动到结束的过程是怎么调度的.其次因为到了Qt4.4版本,Qt的多线程就有所变化,所以本章会以Qt4.0.1和Qt5.6.2版本的源码来进 ...

  7. 推荐系统,深度论文剖析GBDT+LR

    今天我们来剖析一篇经典的论文:Practial Lessons from Predicting Clicks on Ads at Facebook.从这篇paper的名称当中我们可以看得出来,这篇pa ...

  8. C++ 设计模式 4:行为型模式

    0 行为型模式 类或对象怎样交互以及怎样分配职责,这些设计模式特别关注对象之间的通信. 1 模板模式 模板模式(Template Pattern)定义:一个抽象类公开定义了执行它的方法的方式/模板.它 ...

  9. mysql实现当前行的值累加上一行的值

    数据库钱包表有日期.收入.支出三个字段.用mysql语句计算每日余额,得如下结果 select m.*, @total :=@total + 收入 - 支出 as 钱包余额 ( select * fr ...

  10. 内网渗透 day8-linux提权和后门植入

    linux提权和后门植入 目录 1. 脏牛漏洞复现 3 (1) 去网上把代码复制然后touch一个.c文件,vi或者vim打开将代码复制进去保存 3 (2) 进入shell然后从kali开的apach ...