• Java多线程并发系列之闭锁(Latch)和栅栏(CyclicBarrier)



      

    JAVA并发包中有三个类用于同步一批线程的行为,分别是闭锁(Latch),信号灯(Semaphore)和栅栏(CyclicBarrier)。本贴主要说明闭锁(Latch)和栅栏(CyclicBarrier)。

    1. 闭锁(Latch

    闭锁Latch)  —— 确保多个线程在完成各自事务后,才会打开继续执行后面的内容,否则一直等待。

    计数器闭锁CountDownLatch) —— 是JDK5+ 里面闭锁的一个实现,允许一个或多个线程等待某个事件的发生。CountDownLatch  有个正数的计数器,countDown(); 对计数器做减法操作,await(); 等待计数器 = 0。所有await的线程都会阻塞,直到计数器为0或者等待线程中断或者超时。

     1 public static void main(String[] args) throws InterruptedException {
     2         // 申明,等待事件数量 5次
     3         CountDownLatch await = new CountDownLatch(5);
     4  
     5         // 依次创建并启动处于等待状态的5个MyRunnable线程
     6         for (int i = 1; i < 6; ++i) {
     7             new Thread(new MyRunnable(await, i)).start();
     8         }
     9  
    10         System.out.println("等待线程开始工作......");
    11         await.await();
    12         System.out.println("结束!");
    13 }
     1 public static class MyRunnable implements Runnable {
     2  
     3     private final CountDownLatch await;
     4     private final int num;
     5  
     6     public MyRunnable(CountDownLatch await, int num) {
     7         this.await = await;
     8         this.num  =  num;
     9     }
    10  
    11     public void run() {
    12         try {
    13             System.out.println("线程"+num+"执行完毕。");
    14             await.countDown(); // 当前事件执行完毕,计数 -1
    15         } catch (InterruptedException e) {
    16             e.printStackTrace();
    17         }
    18     }
    19 } 
    运行结果:
    等待线程开始工作......
    线程1执行完毕。
    线程2执行完毕。
    线程3执行完毕。
    线程4执行完毕。
    线程5执行完毕。
    结束!

    流程如图所示:

     图1 - CountDownLatch 处理流程

     

    2. 栅栏(CyclicBarrier

    栅栏类似于闭锁,它能阻塞一组线程直到某个事件发生。 栅栏与闭锁的关键区别在于,所有的线程必须同时到达栅栏位置,才能继续执行。闭锁用于等待事件,而栅栏用于等待其他线程。

    场景: 比如甲乙丙三人一把椅子,甲做椅子腿,乙做椅子面,丙做椅子靠背。等3人都做成后,就可以组装成椅子了。这是一种并行迭代,将一个问题分成很多子问题,当一系列的子问题都解决之后(所有子问题线程都已经await(); ),此时将栅栏打开,所有子问题线程被释放,而栅栏位置可以留着下次使用。

     示例如下:

    1 public static void main(String[] args) throws InterruptedException {
    2     // 申明,等待线程数量 3次
    3     CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
    4 
    5     // 依次创建并启动处于等待状态的3个MyRunnable2线程
    6     new Thread(new ChairRunnable(cyclicBarrier, "椅子腿")).start();
    7     new Thread(new ChairRunnable(cyclicBarrier, "椅子面")).start();
    8     new Thread(new ChairRunnable(cyclicBarrier, "椅子背")).start();
    9 }
     1 public static class ChairRunnable implements Runnable {
     2     private final CyclicBarrier cyclicBarrier;
     3     private final String event;
     4 
     5     public ChairRunnable(CyclicBarrier cyclicBarrier, String event) {
     6         this.cyclicBarrier = cyclicBarrier;
     7         this.event = event;
     8     }
     9 
    10     public void run() {
    11         try {
    12             System.out.println("开始做【" + event + "】。");
    13             Thread.sleep(new Random().nextInt(10000));
    14             cyclicBarrier.await(); // 等待其他线程完成
    15         } catch (InterruptedException e) {
    16             e.printStackTrace();
    17         } catch (BrokenBarrierException e) {
    18             e.printStackTrace();
    19         }
    20         System.out.println("【" + event + "】做好了, 我们来一起组装吧!");
    21     }
    22 }
    运行结果:
    开始做【椅子腿】。
    开始做【椅子背】。
    开始做【椅子面】。
    【椅子面】做好了, 我们来一起组装吧!
    【椅子腿】做好了, 我们来一起组装吧!
    【椅子背】做好了, 我们来一起组装吧!

    流程如下图所示:

    图2 - CyclicBarrier 处理流程

  • 相关阅读:
    axb_2019_fmt32 盲打和格式化字符串
    ciscn_2019_final_3 需要避开当前free的chunk的下一个chunk不能超过arena的边界
    xdctf2015_pwn200
    valarray类
    Mysql 常用命令.
    如何处理IO
    等号两边自动添加括号
    Request JSON
    开机小脚本自动打开sublime text 和git-bash
    git 同步勾子
  • 原文地址:https://www.cnblogs.com/steffen/p/11244715.html
Copyright © 2020-2023  润新知