天天看点

Java线程-闭锁(CountDownLatch)

CountDownLatch有时被称为“闭锁”,其作用相当于一扇门:在CountDownLatch达到结束状态之前,这扇门一直是关闭的,并且没有任何线程能通过,当到达结束状态时,这扇门会打开并允许所有的线程通过。当CountDownLatch到达结束状态后,将不会再改变状态,因此这扇门将永远保持打开状态。CountDownLatch可以用来确保某些活动直到其它活动都完成后才继续执行。

她与CyclicBarrier有什么样的区别呢?

(1)、CountDownLatch是不可重置的,因此无法重用;CyclicBarrier没用这种限制,可以重用。

(2)、CountDownLatch的组合是countDown/await,无论是一个线程还是多个线程,只要countDown足够的次数,才能继续下一步,进行后面的任务,它是属于事件驱动的;而CyclicBarrier使用了await,只有当所有的并发线程调用了await,完成所有的任务,才能继续接下来的任务,CyclicBarrier侧重的是线程,而不是事件。

用CountDownLatch实现排队场景:

假设有10个人排队,我们将其分成5个人一批,通过CountDownLatch来协调批次。

public class Thread_CountDownLatch {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(6);
        for (int i = 0; i < 5; i++) {
            Thread t = new Thread(new FirstBatchWorker(latch));
            t.start();
        }
        for (int i= 0; i < 5; i++) {
            Thread t = new Thread(new SecondBatchWorker(latch));
            t.start();
        }
        // 注意:这里是演示目的的逻辑,并不是推荐的协调方式
        while (latch.getCount() != 1) {
            Thread.sleep(100L);
        }
        System.out.println("Wait for first batch finish");
        latch.countDown();
    }
    static class FirstBatchWorker implements Runnable {
        private CountDownLatch latch;
        public FirstBatchWorker(CountDownLatch latch) {
            this.latch = latch;
        }
        @Override
        public void run() {
            System.out.println("First batch executed!");
            latch.countDown();
        }
    }
    static class SecondBatchWorker implements Runnable {
        private CountDownLatch latch;
        public SecondBatchWorker(CountDownLatch latch) {
            this.latch = latch;
        }
        @Override
        public void run() {
            try {
                latch.await();
                System.out.println("Second batch executed!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
           

CountDownLatch的调度方式相对简单,后一批次的线程进行await,等待前一批countDown足够多次。这个例子也从侧面体现出了它的局限性,虽然它也能够支持10个人排队的情况,但是因为不能重用,如果要支持更多人排队,就不能依赖一个CountDownLatch进行了,其编译运行输出如下:

First batch executed!
First batch executed!
First batch executed!
First batch executed!
First batch executed!
Wait for first batch finish
Second batch executed!
Second batch executed!
Second batch executed!
Second batch executed!
Second batch executed!
           

继续阅读