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)的更多相关文章

  1. java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)

    -闭锁(Latch) 闭锁(Latch):一种同步方法,可以延迟线程的进度直到线程到达某个终点状态.通俗的讲就是,一个闭锁相当于一扇大门,在大门打开之前所有线程都被阻断,一旦大门打开所有线程都将通过, ...

  2. 并发编程 04——闭锁CountDownLatch 与 栅栏CyclicBarrier

    Java并发编程实践 目录 并发编程 01—— ThreadLocal 并发编程 02—— ConcurrentHashMap 并发编程 03—— 阻塞队列和生产者-消费者模式 并发编程 04—— 闭 ...

  3. java 多线程并发系列之 生产者消费者模式的两种实现

    在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题.该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度. 为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据 ...

  4. java高并发系列 - 第16天:JUC中等待多线程完成的工具类CountDownLatch,必备技能

    这是java高并发系列第16篇文章. 本篇内容 介绍CountDownLatch及使用场景 提供几个示例介绍CountDownLatch的使用 手写一个并行处理任务的工具类 假如有这样一个需求,当我们 ...

  5. 从火箭发场景来学习Java多线程并发闭锁对象

    从火箭发场景来学习Java多线程并发闭锁对象 倒计时器场景 在我们开发过程中,有时候会使用到倒计时计数器.最简单的是:int size = 5; 执行后,size—这种方式来实现.但是在多线程并发的情 ...

  6. Java高并发系列——检视阅读

    Java高并发系列--检视阅读 参考 java高并发系列 liaoxuefeng Java教程 CompletableFuture AQS原理没讲,需要找资料补充. JUC中常见的集合原来没讲,比如C ...

  7. Java 多线程并发编程一览笔录

    Java 多线程并发编程一览笔录 知识体系图: 1.线程是什么? 线程是进程中独立运行的子任务. 2.创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run ...

  8. Java多线程并发07——锁在Java中的实现

    上一篇文章中,我们已经介绍过了各种锁,让各位对锁有了一定的了解.接下来将为各位介绍锁在Java中的实现.关注我的公众号「Java面典」了解更多 Java 相关知识点. 在 Java 中主要通过使用sy ...

  9. [转]Java多线程干货系列—(一)Java多线程基础

    Java多线程干货系列—(一)Java多线程基础 字数7618 阅读1875 评论21 喜欢86 前言 多线程并发编程是Java编程中重要的一块内容,也是面试重点覆盖区域,所以学好多线程并发编程对我们 ...

随机推荐

  1. 为什么scanf(" %c",&c)中%c前要空格?

    空格确实不是必须的,但有了空格就可以忽略你输入的空格. ****例如:scanf(" %c" ,&c),你输入了' a'(a前面有个空格),a就能被c接受. 但控制符前如果 ...

  2. 根据银行卡号 获取银行名称及银行logo

    根据银行卡号码获取银行卡归属地信息接口地址: https://ccdcapi.alipay.com/validateAndCacheCardInfo.json?_input_charset=utf-8 ...

  3. C++判断输入是否为double

    C++判断输入是否为double 之前写过了Python如何判断输入字符串是否为数字,但是Python是弱类型语言,相比之下C++这种强类型语言判定难度更大. Python判断输入字符串是否为数字的方 ...

  4. 【PHP】PHP基本语法

    一.什么是PHP? a)    定义:PHP就是超文本预处理器 b)    超文本:我们前边8天学习的内容其实就是超文本内容 c)    预处理器:相当于牛奶在工厂加工的过程,我们虽然不可见,但是我们 ...

  5. Android视频悬浮窗口实现

    前言 本文例子实现了点击显示悬浮窗口,同时窗口可播放视频,拖动位置,点击关闭及返回APP页面,通过例子来讲述悬浮窗口实现原理及细节处理,效果图如下所示: 原理 WindowManager对View视图 ...

  6. 让 .NET 轻松构建中间件模式代码

    让 .NET 轻松构建中间件模式代码 Intro 在 asp.net core 中中间件的设计令人叹为观止,如此高大上的设计何不集成到自己的代码里呢. 于是就有了封装了一个简单通用的中间件模板的想法, ...

  7. CSS躬行记(5)——渐变

    渐变是由两种或多种颜色之间的渐进过渡组成,它是一种特殊的图像类型,分为线性渐变和径向渐变,这两类渐变还会细分为单次和重复两种.渐变图像与传统图像相比,它的优势包括占用更少的字节,避免额外的服务器请求, ...

  8. 页面DIV弹出层 JS原生脚本

    <script type="text/javascript"> /*         * 弹出DIV层         */ function showDiv() { ...

  9. xargs与exec区别

    平时使用find命令查找出文件的时候有时候后面有时候用-exec有时候用管道加xargs,这2个到底有什么不同?下面就这2个的不同做简单介绍. 零.实验环境 [root@osker /]# find ...

  10. 数据结构和算法(Golang实现)(4)简单入门Golang-结构体和方法

    结构体和方法 一.值,指针和引用 我们现在有一段程序: package main import "fmt" func main() { // a,b 是一个值 a := 5 b : ...