简介

CountDownLatch可以阻塞某个线程,直到其内部计数器值为0为止或者当其他线程调用了阻塞线程的interrupted方法为止,而内部的计数器需要通过其他线程来减少


示例

public class Main implements Runnable {    private static  CountDownLatch mCountDownLatch =new CountDownLatch(10);
   public Main(){
   }    public static void main(String[] args) {        try {            List<Thread> workers = Stream                    .generate(() -> new Thread(new Main()))                    .limit(10)                    .collect(toList());            workers.forEach(Thread::start);            System.out.println("等待子线程完成工作");            mCountDownLatch.await();            System.out.println("开始下一步"+mCountDownLatch.getCount());        } catch (InterruptedException e) {            e.printStackTrace();        }
   }
   @Override    public void run() {
       try {            Thread.sleep(300);            mCountDownLatch.countDown();            System.out.println(Thread.currentThread().getName() +"完成工作");        } catch (InterruptedException e) {            e.printStackTrace();        }

   }}

构造CountDownLatch时需要传入一个计数值,通过其他线程调用countDown来减少。主线程中通过await方法来阻塞,直到内部计数器为0时或者被中断。


但是,如果在其他线程countDown前发生错误怎么办。可以使用try catch finally语句把他包裹,或者也可以使用await的另一个重载方法,在给定的时间内如果计数器还没为0,那么放弃等待。

mCountDownLatch.await(5,TimeUnit.SECONDS);

public class Main implements Runnable {    private static  CountDownLatch mCountDownLatch =new CountDownLatch(10);
   public Main(){
   }    public static void main(String[] args) {        try {            List<Thread> workers = Stream                    .generate(() -> new Thread(new Main()))                    .limit(10)                    .collect(toList());            workers.forEach(Thread::start);            System.out.println("等待子线程完成工作");            mCountDownLatch.await(5,TimeUnit.SECONDS);            System.out.println("开始下一步"+mCountDownLatch.getCount());        } catch (InterruptedException e) {            e.printStackTrace();        }
   }
   @Override    public void run() {        try {            Thread.sleep(2000);            mCountDownLatch.countDown();        } catch (InterruptedException e) {            e.printStackTrace();        }
   }}

getCount可以获取当前计数器的值


原理

CountDownLatch是通过AQS实现的,CountDownLatch的计数器实际会赋值给AQS的状态变量state

Java并发编程CountDownLatch_java


await方法也是调用了AQS的acquireSharedInterruptibly,这个方法用来获取共享资源时可被中断的方法.,

当发现线程被标明中断后,抛出异常,否则下一步,而tryAcquireShared只是判断了count是不是为0,不为0返回-1,进入doAcquireSharedInterruptibly阻塞当前线程

Java并发编程CountDownLatch_java_02


countDown将调用tryReleaseShared()减1.

tryReleaseShared()方法中通过CAS直到成功完成计数器减1并更新到state为止

Java并发编程CountDownLatch_java_03

通过CAS失败后循环重试,否则判断当前计时器是不是为0,为0就表示最后一个线程调用了countDown,那么还需要通过doReleaseShared唤醒被await方法阻塞的线程,