Java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)
JAVA并发包中有三个类用于同步一批线程的行为,分别是闭锁(Latch),信号灯(Semaphore)和栅栏(CyclicBarrier)。本贴主要说明闭锁(Latch)和栅栏(CyclicBarrier)。
1. 闭锁(Latch)
闭锁(Latch) —— 确保多个线程在完成各自事务后,才会打开继续执行后面的内容,否则一直等待。
计数器闭锁(CountDownLatch) —— 是JDK5+ 里面闭锁的一个实现,允许一个或多个线程等待某个事件的发生。CountDownLatch 有个正数的计数器,countDown(); 对计数器做减法操作,await(); 等待计数器 = 0。所有await的线程都会阻塞,直到计数器为0或者等待线程中断或者超时。
public static void main(String[] args) throws InterruptedException {
// 申明,等待事件数量 5次
CountDownLatch await = new CountDownLatch(5);
// 依次创建并启动处于等待状态的5个MyRunnable线程
for (int i = 1; i < 6; ++i) {
new Thread(new MyRunnable(await, i)).start();
}
System.out.println("等待线程开始工作......");
await.await();
System.out.println("结束!");
}
public static class MyRunnable implements Runnable {
private final CountDownLatch await;
private final int num;
public MyRunnable(CountDownLatch await, int num) {
this.await = await;
this.num = num;
}
public void run() {
try {
System.out.println("线程"+num+"执行完毕。");
await.countDown(); // 当前事件执行完毕,计数 -1
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行结果:
等待线程开始工作......
线程1执行完毕。
线程2执行完毕。
线程3执行完毕。
线程4执行完毕。
线程5执行完毕。
结束!
流程如图所示:

图1 - CountDownLatch 处理流程
2. 栅栏(CyclicBarrier)
栅栏类似于闭锁,它能阻塞一组线程直到某个事件发生。 栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。
场景: 比如甲乙丙三人一把椅子,甲做椅子腿,乙做椅子面,丙做椅子靠背。等3人都做成后,就可以组装成椅子了。这是一种并行迭代,将一个问题分成很多子问题,当一系列的子问题都解决之后(所有子问题线程都已经await(); ),此时将栅栏打开,所有子问题线程被释放,而栅栏位置可以留着下次使用。
示例如下:
public static void main(String[] args) throws InterruptedException {
// 申明,等待线程数量 3次
CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
// 依次创建并启动处于等待状态的3个MyRunnable2线程
new Thread(new ChairRunnable(cyclicBarrier, "椅子腿")).start();
new Thread(new ChairRunnable(cyclicBarrier, "椅子面")).start();
new Thread(new ChairRunnable(cyclicBarrier, "椅子背")).start();
}
public static class ChairRunnable implements Runnable {
private final CyclicBarrier cyclicBarrier;
private final String event;
public ChairRunnable(CyclicBarrier cyclicBarrier, String event) {
this.cyclicBarrier = cyclicBarrier;
this.event = event;
}
public void run() {
try {
System.out.println("开始做【" + event + "】。");
Thread.sleep(new Random().nextInt(10000));
cyclicBarrier.await(); // 等待其他线程完成
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("【" + event + "】做好了, 我们来一起组装吧!");
}
}
运行结果:
开始做【椅子腿】。
开始做【椅子背】。
开始做【椅子面】。
【椅子面】做好了, 我们来一起组装吧!
【椅子腿】做好了, 我们来一起组装吧!
【椅子背】做好了, 我们来一起组装吧!
流程如下图所示:

图2 - CyclicBarrier 处理流程
Java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)的更多相关文章
- java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)
-闭锁(Latch) 闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态.通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻断,一旦大门打开所有线程都将通过, ...
- 并发编程 04——闭锁CountDownLatch 与 栅栏CyclicBarrier
Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...
- java 多线程并发系列之 生产者消费者模式的两种实现
在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题.该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度. 为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据 ...
- java高并发系列 - 第16天:JUC中等待多线程完成的工具类CountDownLatch,必备技能
这是java高并发系列第16篇文章. 本篇内容 介绍CountDownLatch及使用场景 提供几个示例介绍CountDownLatch的使用 手写一个并行处理任务的工具类 假如有这样一个需求,当我们 ...
- 从火箭发场景来学习Java多线程并发闭锁对象
从火箭发场景来学习Java多线程并发闭锁对象 倒计时器场景 在我们开发过程中,有时候会使用到倒计时计数器.最简单的是:int size = 5; 执行后,size—这种方式来实现.但是在多线程并发的情 ...
- Java高并发系列——检视阅读
Java高并发系列--检视阅读 参考 java高并发系列 liaoxuefeng Java教程 CompletableFuture AQS原理没讲,需要找资料补充. JUC中常见的集合原来没讲,比如C ...
- Java 多线程并发编程一览笔录
Java 多线程并发编程一览笔录 知识体系图: 1.线程是什么? 线程是进程中独立运行的子任务. 2.创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run ...
- Java多线程并发07——锁在Java中的实现
上一篇文章中,我们已经介绍过了各种锁,让各位对锁有了一定的了解.接下来将为各位介绍锁在Java中的实现.关注我的公众号「Java面典」了解更多 Java 相关知识点. 在 Java 中主要通过使用sy ...
- [转]Java多线程干货系列—(一)Java多线程基础
Java多线程干货系列—(一)Java多线程基础 字数7618 阅读1875 评论21 喜欢86 前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们 ...
随机推荐
- 1021 Deepest Root (25 分)
A graph which is connected and acyclic can be considered a tree. The height of the tree depends on t ...
- LeetCode | 289. 生命游戏(原地算法/位运算)
记录dalao的位运算骚操作 根据百度百科 ,生命游戏,简称为生命,是英国数学家约翰·何顿·康威在 1970 年发明的细胞自动机. 给定一个包含 m × n 个格子的面板,每一个格子都可以看成是一个细 ...
- ansible--ansible基础
配置文件 ansible的配置文件只有一个,即ansible.cfg,它可以存在于多个地方,ansible读取配置文件的顺序依次是当前命令执行目录->用户家目录下的.ansible.cfg-&g ...
- flask开启调试的四种模式
flask开启调试的四种模式 在app.run()中加一个参数, 'debug=True'就可以开启debug模式 from flask import Flask app = Flask(__name ...
- Linux(Fedora)系统下配制8086汇编环境
1.到www,nasm.us下载nasm 2.解压并安装nasm #tar -xzvf nasm-2.11.08.tar.gz #cd nasm-2.11.08 #./configure #make ...
- 详解java访问修饰符
详解java访问修饰符 为了防止初学者看到因为专业的术语而感觉晦涩难懂,我接下来尽量用生动比喻的说法来解释!首先第一点,我们来讲讲什么叫修饰符!看看这个名称,想想他的意思.修饰符!修饰符!,就是用来修 ...
- Linq 学习——将List集合作为筛选条件查询数据
例: A表是一个List集合,B表也是一个List集合 .A与B有一个共同的字段 RecognitionCarCode B表通过RecognitionCarCode去重后拿到两个值{'1','2'}记 ...
- Linux c++ vim环境搭建系列(6)——CMakeLists.txt多文档多目录组织方法和编写示例
CMakeLists.txt学习 1. 概要 主要是关于cmakelists.txt的编写模板,和多文档多目录的组织方法详解, 涉及第三方库的添加使用方法. 这里主要介绍cmakelists.txt的 ...
- 深入理解JS原型与原型链
函数的prototype 1.函数的prototype属性 *每个函数都有一个prototype属性,它默认指向一个Object空对象(即称为原型对 象) * 原型对象中都有一个属性construct ...
- Extjs简单的form+grid组合
采用的是Extjs4.2版本 http://localhost:49999/GridPanel/Index 该链接是本地连接,只是方便自己访问,读者无法正常访问. <script src=&qu ...