java并发中CountDownLatch的使用

Wesley13
• 阅读 665

java并发中CountDownLatch的使用

在java并发中,控制共享变量的访问非常重要,有时候我们也想控制并发线程的执行顺序,比如:等待所有线程都执行完毕之后再执行另外的线程,或者等所有线程都准备好了才开始所有线程的执行等。

这个时候我们就可以使用到CountDownLatch。

简单点讲,CountDownLatch存有一个放在QueuedSynchronizer中的计数器。当调用countdown() 方法时,该计数器将会减一。然后再调用await()来等待计数器归零。

private static final class Sync extends AbstractQueuedSynchronizer {    ...}private final Sync sync;    public void countDown() {        sync.releaseShared(1);    }

    public void await() throws InterruptedException {        sync.acquireSharedInterruptibly(1);    }    public boolean await(long timeout, TimeUnit unit)        throws InterruptedException {        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));    }

下面我们举两个使用的例子:

主线程等待子线程全都结束之后再开始运行

这里我们定义子线程类,在子线程类里面,我们传入一个CountDownLatch用来计数,然后在子线程结束之前,调用该CountDownLatch的countDown方法。最后在主线程中调用await()方法来等待子线程结束执行。

@Slf4jpublic class MainThreadWaitUsage implements Runnable {    private List<String> outputScraper;    private CountDownLatch countDownLatch;    public MainThreadWaitUsage(List<String> outputScraper, CountDownLatch countDownLatch) {        this.outputScraper = outputScraper;        this.countDownLatch = countDownLatch;    }    @Override    public void run() {        outputScraper.add("Counted down");        countDownLatch.countDown();    }}

看下怎么调用:

    @Test    public void testCountDownLatch()            throws InterruptedException {        List<String> outputScraper = Collections.synchronizedList(new ArrayList<>());        CountDownLatch countDownLatch = new CountDownLatch(5);        List<Thread> workers = Stream                .generate(() -> new Thread(new MainThreadWaitUsage(outputScraper, countDownLatch)))                .limit(5)                .collect(toList());        workers.forEach(Thread::start);        countDownLatch.await();        outputScraper.add("Latch released");        log.info(outputScraper.toString());    }

执行结果如下:

07:37:27.388 [main] INFO MainThreadWaitUsageTest - [Counted down, Counted down, Counted down, Counted down, Counted down, Latch released]

等待所有线程都准备好再一起执行

上面的例子中,我们是主线程等待子线程,那么在这个例子中,我们将会看看怎么子线程一起等待到准备好的状态,再一起执行。

思路也很简单,在子线程开始之后,将等待的子线程计数器减一,在主线程中await该计数器,等计数器归零之后,主线程再通知子线程运行。

public class ThreadWaitThreadUsage implements Runnable {    private List<String> outputScraper;    private CountDownLatch readyThreadCounter;    private CountDownLatch callingThreadBlocker;    private CountDownLatch completedThreadCounter;    public ThreadWaitThreadUsage(            List<String> outputScraper,            CountDownLatch readyThreadCounter,            CountDownLatch callingThreadBlocker,            CountDownLatch completedThreadCounter) {        this.outputScraper = outputScraper;        this.readyThreadCounter = readyThreadCounter;        this.callingThreadBlocker = callingThreadBlocker;        this.completedThreadCounter = completedThreadCounter;    }    @Override    public void run() {        readyThreadCounter.countDown();        try {            callingThreadBlocker.await();            outputScraper.add("Counted down");        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            completedThreadCounter.countDown();        }    }}

看下怎么调用:

    @Test    public void testCountDownLatch()            throws InterruptedException {        List<String> outputScraper = Collections.synchronizedList(new ArrayList<>());        CountDownLatch readyThreadCounter = new CountDownLatch(5);        CountDownLatch callingThreadBlocker = new CountDownLatch(1);        CountDownLatch completedThreadCounter = new CountDownLatch(5);        List<Thread> workers = Stream                .generate(() -> new Thread(new ThreadWaitThreadUsage(                        outputScraper, readyThreadCounter, callingThreadBlocker, completedThreadCounter)))                .limit(5)                .collect(toList());        workers.forEach(Thread::start);        readyThreadCounter.await();        outputScraper.add("Workers ready");        callingThreadBlocker.countDown();        completedThreadCounter.await();        outputScraper.add("Workers complete");        log.info(outputScraper.toString());    }

输出结果如下:

07:41:47.861 [main] INFO ThreadWaitThreadUsageTest - [Workers ready, Counted down, Counted down, Counted down, Counted down, Counted down, Workers complete]

停止CountdownLatch的await

如果我们调用await()方法,该方法将会等待一直到count=0才结束。但是如果在线程执行过程中出现了异常,可能导致countdown方法执行不了。那么await()方法可能会出现无限等待的情况。

这个时候我们可以使用:

    public boolean await(long timeout, TimeUnit unit)        throws InterruptedException {        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));    }

本文的例子可以参考https://github.com/ddean2009/learn-java-concurrency/tree/master/CountDownLatch

更多教程请参考 flydean的博客

本文分享自微信公众号 - 程序那些事(flydean-tech)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
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
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Wesley13 Wesley13
3年前
java 线程篇 之CyclicBarrier、CountDownLatch、Semaphore
java提供了很多控制线程到达某一状态导致之前阻塞线程运行的函数,这些在控制任务执行提供了很大的便利,比如在zookper使用Semaphore实现分布式锁1、CountDownLatchcountDownLatch提供await(),CountDownLatch()来控制,前面我很多例子,使用这个来模拟多线程运行的,所以这里不过多介绍2
Wesley13 Wesley13
3年前
java并发编程之二
CountDownLatch类  允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助。  CountDownLatch能够使一个线程在等待另外一些线程完成各自工作之后,再继续执行。使用一个计数器进行实现。计数器初始值为线程的数量。当每一个线程完成自己任务后,计数器的值就会减一。当计数器的值为0时,表示所有的线程都已经完成了任务,然后在
Wesley13 Wesley13
3年前
java多线程测试性能,总线程使用总时间。
前段时间在做java多线程,然后涉及到了一个效率的问题。  java多线程中,所有线程执行完毕,总共使用多少时间呢?importjava.util.concurrent.CountDownLatch;  countDwonlatch提供了最常用的一些测试方法。创建一个countDownLatch对象:CountDown
Wesley13 Wesley13
3年前
Java多线程并发控制工具CountDownLatch,实现原理及案例
闭锁(CountDownLatch)是Java多线程并发中的一种同步器,它是JDK内置的同步器。通过它可以定义一个倒计数器,当倒计数器的值大于0时,所有调用await方法的线程都会等待。而调用countDown方法则可以让倒计数器的值减一,当倒计数器值为0时所有等待的线程都将继续往下执行。闭锁的主要应用场景是让某个或某些线程在某个运行节点上等待N个条件都
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这