JUC包下的CountdownLatch类

CountdownLatch简介

中文是倒计时门闩的意思,该类允许一个或多个线程一直等待,直到其他线程运行完成后再执行。CountDownLatch 里面使用了计数器和阻塞队列, 当计数器的值递减为0之前,队列里面的线程处于阻塞状态,当计数器递减到0时会唤醒阻塞队列所有线程,这里的计数器是一个标志,可以表示一个任务一个线程,也可以表示一个倒计时器,CountDownLatch可以解决那些一个或者多个线程在执行之前必须依赖于某些必要的前提业务先执行的场景。

下面程序main方法中最后的结束打印操作会等另外3个线程运行结束计数归零后才会执行。

//创建对象,等待计数值为3
CountDownLatch latch = new CountDownLatch(3);

ExecutorService executorService = Executors.newFixedThreadPool(3);

//向线程池中放入三个任务,睡眠时间采取随机值
for (int i = 0; i < 3; i++) {
    executorService.submit(() -> {
        try {
            System.out.println(Thread.currentThread().getName() + "开始");
            TimeUnit.SECONDS.sleep(ThreadLocalRandom.current().nextInt(8));
            latch.countDown();//计数减一,最好放到finally中
            System.out.println(Thread.currentThread().getName() + "结束:" + latch.getCount());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    });
}

System.out.println("等待");
try {
    //等待计数归零,然后再继续执行后面代码
    latch.await();
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("结束");
executorService.shutdown();

下面通过CountdownLatch模拟王者玩家等待加载的效果,全部玩家加载完毕之后,才能打印敌军还有五秒到达战场。里面使用了AtomicInteger自增值给玩家命名为玩家0,玩家1…

//用来给线程命名
AtomicInteger num = new AtomicInteger();

//创建线程池并给线程命名
ExecutorService executorService = Executors.newFixedThreadPool(10, r ->
        new Thread(r, "玩家" + num.getAndIncrement())
);

CountDownLatch latch = new CountDownLatch(10);
//创建玩家数组
String[] player = new String[10];

for (int j = 0; j < 10; j++) {
    int index = j;
    executorService.submit(() -> {
        for (int i = 0; i <= 100; i++) {
            try {
                //随机睡眠一段时间
                Thread.sleep(ThreadLocalRandom.current().nextInt(100));
            } catch (InterruptedException e) {
            }

            player[index] = Thread.currentThread().getName() + "(" + (i + "%") + ")";
            System.out.print("\r" + Arrays.toString(player));
        }
        latch.countDown();
    });
}
//等待归零
latch.await();
System.out.println("\n欢迎来到王者荣耀,敌军还有五秒到达战场");

executorService.shutdown();

CountdownLatch工作流程

如下图所示,t1,t2,t3三个线程在全部执行到某个点之后,才会允许其他线程执行。CountdownLatch底层使用了AQS进行计数和唤醒线程