少侠,用过CountDownLatch吗?

       今天在看Android源码的时候,看到了CountDownLatch,第一回见到(可能是我太菜,源码看的少)不知道是干啥的,所以去查了一下。这不查不知道,一查吓一跳啊~原来还有这么方便个玩意儿!回想起来,开发过程中有很多需要用到的地方,以前是自己写,知道了这个之后,可以用”官方“的啦

CountDownLatch是干啥的?

先来个比较正统的解释:

       CountDownLatch是java.util.concurrent包中一个类,CountDownLatch主要提供的机制是多个线程都达到了预期状态或者完成了预期工作时触发事件,其他线程可以等待这个事件来触发自己后续的工作。 等待的线程可以是多个,即CountDownLatch可以唤醒多个等待的线程。到达自己预期状态的线程会调用CountDownLatch的countDown方法,而等待的线程会调用CountDownLatch的await方法。

是不是看起来云里雾里的?我来个举个栗子:

       假如我是个老司机,我要开车,我要载三个乘客。这三个乘客从家里出门往我车上赶,就像代码里的异步一样,我需要等乘客都到了才能发车,那我咋知道啥时候他们都到了呢?有人说:你这不是傻吗,你看不见吗?对,我就是看不见,因为我在睡觉。然后又有人说了:那你不能定个闹表吗?是可以定闹表,就像代码中的sleep一样,但是,万一大家都早到了,或者有人迟到了,咋整?这时,就需要有个东西帮忙,在乘客都到了的时候,唤醒我,这个玩意儿就是CountDownLatch

CountDownLatch咋用?

还是接着老司机的例子,既然我要实现上面的需求,那我得告诉它,我需要等几个乘客。这个是在CountDownLatch的构造中传入的:

CountDownLatch countDownLatch = new CountDownLatch(3);

CountDownLatch的创建就是这么简单,然后就是:乘客来了,该怎么告诉CountDownLatch呢?

countDownLatch.countDown();

最后一个问题,司机该如何和CountDownLatch交流呢?
       首先,司机调用CountDownLatch的await()方法,然后司机就睡觉去了,注意,这里不是开玩笑,线程是真的睡了,所以可别再Android的主线程这么搞,注意人身安全。然后,当乘客一个一个都到达了,CountDownLatch就会唤醒司机,司机可以发车啦~
await有一个重载方法,是带超时的,很人性化~

public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}

/**
 * @return 如果倒数到0唤醒的为true,如果是超时唤醒的,为false
 */
public boolean await(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

       好的!CountDownLatch使用起来就这么简单,是不是很方便呢?这里强调一下,司机可以是多个,也就是说,可以有多条线程在等待,等倒数到0后一起唤醒!

Demo

最后,附上demo和log日志,方便大家理解~

public class CountDownLatchDemo {
    private static final CountDownLatch mCountDownLatch = new CountDownLatch(3);
    private static SimpleDateFormat mDataFormat = new SimpleDateFormat("HH:mm:ss");

    public static void main(String[] args) throws InterruptedException {

        //创建3个乘客
        for (int i = 0; i < 3; ++i) {
            new Thread(new Passenger(), "乘客" + i).start();
        }
        System.out.println(mDataFormat.format(new Date()) + ":我是司机,我先睡了,乘客都来了叫我");
        mCountDownLatch.await();
        System.out.println(mDataFormat.format(new Date()) + ":我是司机,我被叫醒了,乘客都来了,可以发车了");
    }

    public static class Passenger implements Runnable {

        @Override
        public void run() {
            try {
                Thread.sleep(new Random().nextInt(10) * 1000);
                System.out.println(mDataFormat.format(new Date()) + ":我是" + Thread.currentThread().getName() + ",我上车啦!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                //计数减一
                //放在finally避免任务执行过程出现异常,导致countDown()不能被执行
                mCountDownLatch.countDown();
            }
        }
    }
}

输出日志:

21:13:55:我是司机,我先睡了,乘客都来了叫我
21:13:58:我是乘客2,我上车啦!
21:14:03:我是乘客1,我上车啦!
21:14:04:我是乘客0,我上车啦!
21:14:04:我是司机,我被叫醒了,乘客都来了,可以发车了