Java并发新构件之CounDownLatch

    CountDownLatch主要用于同步一个或多个任务,强制它们等待由其他任务执行的一组操作完成。

    **你可以向CountDownLatch对象设置一个初始计数值,任何在这个对象上调用await()的方法都将阻塞,直到这个计数值达到0。**其他任务在结束其工作时,可以在该对象上调用countDown()来减小这个计数值,你可以通过调用getCount()方法来获取当前的计数值。CountDownLatch被设计为只触发一次,计数值不能被重置。如果你需要能够重置计数值的版本,则可以使用CyclicBarrier。

    调用countDown()的任务在产生这个调用时并没有阻塞,只有对await()的调用会被阻塞,直到计数值到达0。

    CountDownLatch的典型用法是将一个程序分为n个互相独立的可解决任务,并创建值为0的CountDownLatch。当每个任务完成时,都会在这个锁存器上调用countDown()。等待问题被解决的任务在这个锁存器上调用await(),将它们自己锁住,直到锁存器计数结束。下面是演示这种技术的一个框架示例:

1import java.util.Random; 2import java.util.concurrent.CountDownLatch; 3import java.util.concurrent.DelayQueue; 4import java.util.concurrent.ExecutorService; 5import java.util.concurrent.Executors; 6import java.util.concurrent.TimeUnit; 7 8class TaskPortion implements Runnable { 9    private static int counter = 0; 10    private final int id = counter++; 11    private static Random random = new Random(); 12    private final CountDownLatch latch; 13    public TaskPortion(CountDownLatch latch) { 14        this.latch = latch; 15    } 16    @Override 17    public void run() { 18        try { 19            doWork(); 20            latch.countDown();//普通任务执行完后,调用countDown()方法,减少count的值 21            System.out.println(this + " completed. count=" + latch.getCount()); 22        } catch (InterruptedException e) { 23             24        } 25    } 26     27    public void doWork() throws InterruptedException { 28        TimeUnit.MILLISECONDS.sleep(random.nextInt(2000)); 29    } 30     31    @Override 32    public String toString() { 33        return String.format("%1$-2d ", id); 34    } 35} 36 37class WaitingTask implements Runnable { 38    private static int counter = 0; 39    private final int id = counter++; 40    private final CountDownLatch latch; 41    public WaitingTask(CountDownLatch latch) { 42        this.latch = latch; 43    } 44    @Override 45    public void run() { 46        try { 47            //这些后续任务需要等到之前的任务都执行完成后才能执行,即count=0时 48            latch.await(); 49            System.out.println("Latch barrier passed for " + this); 50        } catch (InterruptedException e) { 51            System.out.println(this + " interrupted."); 52        } 53    } 54     55    @Override 56    public String toString() { 57        return String.format("WaitingTask %1$-2d ", id); 58    } 59} 60 61public class CountDownLatchDemo { 62    static final int SIZE = 10; 63    public static void main(String[] args) { 64        CountDownLatch latch = new CountDownLatch(SIZE); 65        ExecutorService exec = Executors.newCachedThreadPool(); 66        //5个WaitingTask 67        for (int i = 0; i < 5; i++) { 68            exec.execute(new WaitingTask(latch)); 69        } 70        //10个任务,这10个任务要先执行才会执行WaitingTask 71        for (int i = 0; i < SIZE; i++) { 72            exec.execute(new TaskPortion(latch)); 73        } 74        System.out.println("Launched all tasks."); 75        exec.shutdown();//当所有的任务都结束时,关闭exec 76    } 77}

执行结果(可能的结果):

1Launched all tasks. 24   completed. count=9 36   completed. count=8 43   completed. count=7 50   completed. count=6 62   completed. count=5 71   completed. count=4 85   completed. count=3 97   completed. count=2 109   completed. count=1 118   completed. count=0 12Latch barrier passed for WaitingTask 0   13Latch barrier passed for WaitingTask 2   14Latch barrier passed for WaitingTask 1   15Latch barrier passed for WaitingTask 3   16Latch barrier passed for WaitingTask 4

    从结果中可以看到,所有的WaitingTask都是在所有的TaskPortion执行完成之后执行的。

    TaskPortion将随机的休眠一段时间,以模拟这部分工作的完成。而WaitingTask表示系统中必须等待的部分,它要等到问题的初始部分完成后才能执行。注意:所有任务都使用了在main()中定义的同一个CountDownLatch对象

一个更实际的例子(参考自这里,有改动):N个选手比赛跑步,在枪响后同时起跑,全部到达终点后比赛结束,下面是代码:

1import java.util.concurrent.CountDownLatch; 2import java.util.concurrent.ExecutorService; 3import java.util.concurrent.Executors; 4 5/** 6 * 用CountDownLatch来模拟运动员赛跑的Demmo. 7 */ 8public class CountDownLatchDemo { 9 /** 参与比赛的人数(并发线程数) */ 10 private static int PLAYER_NUM = 8; 11 12 public static void main(String[] args) throws Exception { 13 //用于让主线程(裁判)等待所有子线程(运动员)就绪 14 final CountDownLatch readySignal = new CountDownLatch(PLAYER_NUM); 15 //用于让子线程(运动员)等待主线程(裁判)发号施令 16 final CountDownLatch beginSignal = new CountDownLatch(1); 17 //用于让主线程(裁判)等待所有子线程(运动员)执行(比赛)完成 18 final CountDownLatch endSignal = new CountDownLatch(PLAYER_NUM); 19 ExecutorService executorService = Executors.newFixedThreadPool(PLAYER_NUM); 20 21 for(int i = 0; i < PLAYER_NUM; i++){ 22 final int num = i + 1; 23 Runnable player = new Runnable(){ 24 @Override 25 public void run() { 26 System.out.println("No." + num + " is ready"); 27 //一个任务就绪后,减少readySignal上的等待 28 readySignal.countDown(); 29 try { 30 //等待主线程比赛开始的命令 31 beginSignal.await(); 32 System.out.println("No." + num + " is running"); 33 Thread.sleep((long) (Math.random() * 5000)); 34 System.out.println("No.-----" + num + " arrived"); 35 } catch (InterruptedException e) { 36 e.printStackTrace(); 37 } finally { 38 //每个线程执行完成后,调用countDown减少在endSignal上的等待线程数 39 endSignal.countDown(); 40 } 41 } 42 }; 43 executorService.execute(player); 44 } 45 46 //这里让主线程等待,确保所有子线程已开始执行并调用了await进入等待状态 47 readySignal.await(); 48 System.out.println("Game Start!"); 49 //所有等待在beginSignal上的线程(运动员)开始执行(比赛) 50 beginSignal.countDown(); 51 try { 52 //等待所有在endSignal上的线程(运动员)执行(比赛)完成 53 endSignal.await(); 54 } catch (InterruptedException e) { 55 e.printStackTrace(); 56 } finally { 57 System.out.println("Game Over!"); 58 executorService.shutdown(); 59 } 60 } 61}

代码中用到了3个CountDownLatch对象,具体作用已在代码中说明,这里不赘述。

说一下执行过程:

  1. 主线程(main方法)启动后,向ExecutorService中提交了8个任务(子线程),每个任务开始执行后都在等待主线程说“比赛开始”。
  2. 主线程等待线程池中的任务全部都处于“ready”状态,然后鸣枪,比赛开始。
  3. 主线程等待所有任务执行完成。
  4. 子线程各自执行,然后陆续到达终点,完成比赛。
  5. 主线程宣告“比赛结束”,输出“Game Over!”。

下面是执行结果:

1No.1 is ready 2No.2 is ready 3No.3 is ready 4No.4 is ready 5No.6 is ready 6No.5 is ready 7No.7 is ready 8No.8 is ready 9Game Start! 10No.1 is running 11No.4 is running 12No.3 is running 13No.6 is running 14No.2 is running 15No.5 is running 16No.7 is running 17No.8 is running 18No.-----3 arrived 19No.-----2 arrived 20No.-----1 arrived 21No.-----8 arrived 22No.-----4 arrived 23No.-----6 arrived 24No.-----7 arrived 25No.-----5 arrived 26Game Over!
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java日期时间API系列31

  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前

java并发编程之二

CountDownLatch类  允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。  CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成了任务,然后在

Java并发新构件之CounDownLatch - HelloWorld