一、CyclicBarrier

CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。

CyclicBarrier类似于CountDownLatch也是个计数器, 不同的是CyclicBarrier数的是调用了CyclicBarrier.await()进入等待的线程数, 当线程数达到了CyclicBarrier初始时规定的数目时,所有进入等待状态的线程被唤醒并继续。 CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。 CyclicBarrier初始时还可带一个Runnable的参数,此Runnable任务在CyclicBarrier的数目达到后,所有其它线程被唤醒前被执行。

构造方法摘要:

造方法摘要
CyclicBarrier(int parties) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在每个 barrier 上执行预定义的操作。
CyclicBarrier(int parties, Runnable barrierAction) 创建一个新的 CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier 时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行

方法摘要

返回值 方法
int await() 在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
int await(long timeout, TimeUnit unit) 在所有参与者都已经在此屏障上调用 await 方法之前,将一直等待。
int getNumberWaiting() 返回当前在屏障处等待的参与者数目。
int getParties() 返回要求启动此 barrier 的参与者数目。
boolean isBroken() 查询此屏障是否处于损坏状态。
void reset() 将屏障重置为其初始状态。

代码示例 :

示例一:

import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore; public class CyclicBarrierTest { public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
final CyclicBarrier cb = new CyclicBarrier(3);//创建CyclicBarrier对象并设置3个公共屏障点
for(int i=0;i<3;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将到达集合地点1,当前已有" + cb.getNumberWaiting() + "个已经到达,正在等候");
cb.await();//到此如果没有达到公共屏障点,则该线程处于等待状态,如果达到公共屏障点则所有处于等待的线程都继续往下运行 Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将到达集合地点2,当前已有" + cb.getNumberWaiting() + "个已经到达,正在等候");
cb.await();
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将到达集合地点3,当前已有" + cb.getNumberWaiting() + "个已经到达,正在等候");
cb.await();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
}
}

输出:

线程pool-1-thread-2即将到达集合地点1,当前已有0个已经到达,正在等候
线程pool-1-thread-1即将到达集合地点1,当前已有1个已经到达,正在等候
线程pool-1-thread-3即将到达集合地点1,当前已有2个已经到达,正在等候
线程pool-1-thread-3即将到达集合地点2,当前已有0个已经到达,正在等候
线程pool-1-thread-1即将到达集合地点2,当前已有1个已经到达,正在等候
线程pool-1-thread-2即将到达集合地点2,当前已有2个已经到达,正在等候
线程pool-1-thread-3即将到达集合地点3,当前已有0个已经到达,正在等候
线程pool-1-thread-2即将到达集合地点3,当前已有1个已经到达,正在等候
线程pool-1-thread-1即将到达集合地点3,当前已有2个已经到达,正在等候

示例二:

如果在构造CyclicBarrier对象的时候传了一个Runnable对象进去,则每次到达公共屏障点的时候都最先执行这个传进去的Runnable,然后再执行处于等待的Runnable。如果把上面的例子改成下面这样:

package com.thread;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore; public class CyclicBarrierTest { public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
//final CyclicBarrier cb = new CyclicBarrier(3);//创建CyclicBarrier对象并设置3个公共屏障点
final CyclicBarrier cb = new CyclicBarrier(3,new Runnable(){
@Override
public void run() {
System.out.println("********我最先执行***********");
}
});
for(int i=0;i<3;i++){
Runnable runnable = new Runnable(){
public void run(){
try {
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将到达集合地点1,当前已有" + cb.getNumberWaiting() + "个已经到达,正在等候");
cb.await();//到此如果没有达到公共屏障点,则该线程处于等待状态,如果达到公共屏障点则所有处于等待的线程都继续往下运行 Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将到达集合地点2,当前已有" + cb.getNumberWaiting() + "个已经到达,正在等候");
cb.await(); //这里CyclicBarrier对象又可以重用
Thread.sleep((long)(Math.random()*10000));
System.out.println("线程" + Thread.currentThread().getName() +
"即将到达集合地点3,当前已有" + cb.getNumberWaiting() + "个已经到达,正在等候");
cb.await();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
service.shutdown();
}
}

结果

线程pool-1-thread-1即将到达集合地点1,当前已有0个已经到达,正在等候
线程pool-1-thread-3即将到达集合地点1,当前已有1个已经到达,正在等候
线程pool-1-thread-2即将到达集合地点1,当前已有2个已经到达,正在等候
********我最先执行***********
线程pool-1-thread-1即将到达集合地点2,当前已有0个已经到达,正在等候
线程pool-1-thread-3即将到达集合地点2,当前已有1个已经到达,正在等候
线程pool-1-thread-2即将到达集合地点2,当前已有2个已经到达,正在等候
********我最先执行***********
线程pool-1-thread-1即将到达集合地点3,当前已有0个已经到达,正在等候
线程pool-1-thread-3即将到达集合地点3,当前已有1个已经到达,正在等候
线程pool-1-thread-2即将到达集合地点3,当前已有2个已经到达,正在等候
********我最先执行***********

二、CountDownLatch

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。

CountDownLatch 是一个通用同步工具,它有很多用途。将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,或入口:在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待。用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。

CountDownLatch 的一个有用特性是,它不要求调用 countDown 方法的线程等到计数到达零时才继续,而在所有线程都能通过之前,它只是阻止任何线程继续通过一个 await。

构造方法摘要 
CountDownLatch(int count) 构造一个用给定计数初始化的 CountDownLatch。

方法摘要

返回值 方法
void await() 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
boolean await(long timeout, TimeUnit unit) 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
void countDown() 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
long getCount() 返回当前计数。
String toString() 返回标识此锁存器及其状态的字符串。

代码示例

一种典型用法是,将一个问题分成 N 个部分,用执行每个部分并让锁存器倒计数的 Runnable 来描述每个部分,然后将所有 Runnable 加入到 Executor 队列。当所有的子部分完成后,协调线程就能够通过 await。(当线程必须用这种方法反复倒计数时,可改为使用 CyclicBarrier。)

示例一:

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class CountdownLatchTest1 { public static void main(String[] args) {
ExecutorService service = Executors. newFixedThreadPool(3);
final CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
Runnable runnable = new Runnable() { @Override
public void run() {
try {
System. out.println("子线程" + Thread.currentThread().getName() + "开始执行");
Thread. sleep((long) (Math. random() * 10000));
System. out.println("子线程" + Thread.currentThread().getName() + "执行完成");
latch.countDown(); // 当前线程调用此方法,则计数减一
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
} try {
System. out.println("主线程" + Thread.currentThread().getName() + "等待子线程执行完成..." );
latch.await(); // 阻塞当前线程,直到计时器的值为0
System. out.println("主线程" + Thread.currentThread().getName() + "开始执行...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

示例二:百米赛跑,4名运动员选手到达场地等待裁判口令,裁判一声口令,选手听到后同时起跑,当所有选手到达终点,裁判进行汇总汇总排名。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; public class CountdownLatchTest2 { public static void main(String[] args) {
ExecutorService service = Executors. newCachedThreadPool();
final CountDownLatch cdOrder = new CountDownLatch(1);
final CountDownLatch cdAnswer = new CountDownLatch(4);
for (int i = 0; i < 4; i++) {
Runnable runnable = new Runnable() {
public void run() {
try {
System. out.println("选手" + Thread.currentThread().getName() + "正等待裁判发布口令");
cdOrder.await();
System. out.println("选手" + Thread.currentThread().getName() + "已接受裁判口令");
Thread. sleep((long) (Math. random() * 10000));
System. out.println("选手" + Thread.currentThread().getName() + "到达终点");
cdAnswer.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
Thread. sleep((long) (Math. random() * 10000)); System. out.println("裁判" + Thread.currentThread ().getName() + "即将发布口令" );
cdOrder.countDown();
System. out.println("裁判" + Thread.currentThread ().getName() + "已发送口令,正在等待所有选手到达终点" );
cdAnswer.await();
System. out.println("所有选手都到达终点" );
System. out.println("裁判" + Thread.currentThread ().getName() + "汇总成绩排名" );
} catch (Exception e) {
e.printStackTrace();
}
service.shutdown(); }
}

三、CountDownLatch与CyclicBarrier对比

CountDownLatch CyclicBarrier
减计数方式 加计数方式
计算为0时释放所有等待的线程 计数达到指定值时释放所有等待线程
计数为0时,无法重置 计数达到指定值时,计数置为0重新开始
调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响 调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞
不可重复利用 可重复利用

其它学习资料:

CyclicBarrier的用法 
CountDownLatch(倒计时计数器)使用说明

CyclicBarrier和CountDownLatch区别

https://www.cnblogs.com/pony1223/p/9303016.html

JAVA多线程学习十三 - 同步工具CyclicBarrier与CountDownLatch的更多相关文章

  1. JAVA多线程提高十:同步工具CyclicBarrier与CountDownLatch

    今天继续学习其它的同步工具:CyclicBarrier与CountDownLatch 一.CyclicBarrier CyclicBarrier是一个同步辅助类,它允许一组线程互相等待,直到到达某个公 ...

  2. Java八个并发学习——线程同步工具CyclicBarrier

    本文是一篇文章对网络的研究摘要,感谢您的无私分享. CyclicBarrier 类有一个整数初始值,此值表示将在同一点同步的线程数量.当当中一个线程到达确定点,它会调用await() 方法来等待其它线 ...

  3. JAVA多线程提高十三:同步集合类的应用

    1.引言 在多线程的环境中,如果想要使用容器类,就需要注意所使用的容器类是否是线程安全的.在最早开始,人们一般都在使用同步容器(Vector,HashTable),其基本的原理,就是针对容器的每一个操 ...

  4. JAVA多线程提高十一:同步工具Exchanger

    Exchanger可以在对中对元素进行配对和交换的线程的同步点.每个线程将条目上的某个方法呈现给 exchange 方法,与伙伴线程进行匹配,并且在返回时接收其伙伴的对象.Exchanger 可能被视 ...

  5. “全栈2019”Java多线程第二十三章:活锁(Livelock)详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  6. “全栈2019”Java多线程第十三章:线程组ThreadGroup详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  7. Java多线程学习(六)Lock锁的使用

    系列文章传送门: Java多线程学习(二)synchronized关键字(1) Java多线程学习(二)synchronized关键字(2) Java多线程学习(三)volatile关键字 Java多 ...

  8. Java多线程学习(转载)

    Java多线程学习(转载) 时间:2015-03-14 13:53:14      阅读:137413      评论:4      收藏:3      [点我收藏+] 转载 :http://blog ...

  9. java多线程学习笔记——详细

    一.线程类  1.新建状态(New):新创建了一个线程对象.        2.就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start()方法.该状态的线程位于可运行线程池中, ...

随机推荐

  1. Capstone CS5265替代LT8711龙迅|Typec转HDMI4K60HZ投屏转换方案

    LT8711是一款高性能C型/DP1.2至HDMI2.0转换器,设计用于将USB typec或DP1.2源连接至HDMI2.0接收器.LT8711集成了兼容DP1.2的接收机和兼容HDMI2.0的发射 ...

  2. <数据结构>XDOJ321.高铁网络

    问题与解答 描述: 国家建设高铁网络,网络由一些连接城市的高铁线路构成.现有高铁建设情况可列为一张统计表,表中列出了每一条高铁线路直接连接的两个城市.国家的建设目标是全国每两个城市之间都可以实现高铁交 ...

  3. 简单学生管理系统HTML前端页面

    效果图: 实现代码: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> ...

  4. emqx的acl.conf使用

    allow_anonymous=true就不说了,打开这个就像开了挂 现在讨论一下allow_anonymous=false,这样的话你会发现,client连接不上了 后来发现连接和权限 是两个事.. ...

  5. .net core中Grpc使用报错:The response ended prematurely.

    当我们调用Grpc是出现下面的一堆异常时,一般是由于LTS导致的: Call failed with gRPC error status. Status code: 'Unavailable', Me ...

  6. 通过USB和wifi连接真机&编写第一个脚本

    一.通过USB和wifi连接真机 1. 数据线连接手机并允许调试 cmd命令行执行:adb devices (能查看到设备就可以进行编写自动化脚本步骤了,如果在USB接触不良的情况下,可以执行下面步骤 ...

  7. 华为云 Kubernetes 管理员实训 五 课后作业

    练习1 部署一个Deployment应用,使用secret普通卷,该应用启动依赖此secret. Deployment的名称为<hwcka-005-1-你的华为云id> 将所用命令.创建的 ...

  8. ajax 异步 提交 含文件的表单

    1.前言 需求是使用 jquery 的 ajax 异步提交表单,当然,不是简单的数据,而是包含文件数据的表单.于是我想到了 new FormData() 的用法, 可是仍然提交失败,原来是ajax的属 ...

  9. PPT2010制作充电动画

    原文: https://www.toutiao.com/i6492264647318569486/ 启动PPT2010,新建一张空白幻灯片 选择"插入"选项卡,"插图&q ...

  10. 微软开源Kubernetes服务网格项目Open Service Mesh​

    尽管微服务环境提供可移植性,允许更快更频繁的部署周期,甚至还能让组织创建关注于特定领域的团队,但这也伴随着对于流量管理.安全以及可观测性等需求的增长.在整个生态系统中,针对这些需求的服务网格模式的实现 ...