CountDownLatch

CountDownLatch是在java1.5被引入的,跟它一起被引入的并发工具类还有CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue,它们都存在于java.util.concurrent包下。CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。
CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务。

调用CountDownLatch类的await()方法会一直阻塞,直到其他线程调用CountDown()方法使计数器的值减1,当计数器的值等于0则当因调用await()方法处于阻塞状态的线程会被唤醒继续执行。计数器是不能被重置的。这个类使用线程在达到某个条件后继续执行的情况。比如并行计算,计算量特别大,我可以将计算量拆分成多个线程进行计算,最后将结果汇总。

一个CountdownLatch 例子

@Slf4jpublic class CountDownLatchExample1 {    private final static int threadCount = 200;    public static void main(String[] args) throws Exception {        ExecutorService exec = Executors.newCachedThreadPool();        final CountDownLatch countDownLatch = new CountDownLatch(threadCount);        for (int i = 0; i < threadCount; i++) {            final int threadNum = i;            exec.execute(() -> {                try {                    test(threadNum);                } catch (Exception e) {                    log.error("exception", e);                } finally {                    countDownLatch.countDown();                }            });        }        countDownLatch.await();        log.info("finish");        exec.shutdown();    }    private static void test(int threadNum) throws Exception {        Thread.sleep(100);        log.info("{}", threadNum);        Thread.sleep(100);    }}

我们在线程之后都调用了countDown方法,在执行log之前调用了await方法,从而来保证打印日志时一定是在所有线程执行完。假设我们不使用CountDownLatch时结果会怎么样?

@Slf4jpublic class CountDownLatchExample1 {    private final static int threadCount = 200;    public static void main(String[] args) throws Exception {        ExecutorService exec = Executors.newCachedThreadPool();        final CountDownLatch countDownLatch = new CountDownLatch(threadCount);        for (int i = 0; i < threadCount; i++) {            final int threadNum = i;            exec.execute(() -> {                try {                    test(threadNum);                } catch (Exception e) {                    log.error("exception", e);                }//                finally {////                    countDownLatch.countDown();//                }            });        }//        countDownLatch.await();        log.info("finish");//        exec.shutdown();    }    private static void test(int threadNum) throws Exception {        Thread.sleep(100);        log.info("{}", threadNum);        Thread.sleep(100);    }}

这段代码打印结果就是,一开始就打印出了finish,因为这个打印是在主线程中执行。