美文网首页程序员
JUC源码分析-JUC锁(三):CountDownLatch

JUC源码分析-JUC锁(三):CountDownLatch

作者: 泰迪的bagwell | 来源:发表于2017-12-14 21:29 被阅读0次

    1. 概述

    CountDownLatch是一个同步辅助类,通过AQS实现的一个闭锁。在其他线程完成它们的操作之前,允许一个多个线程等待。简单来说,CountDownLatch中有一个锁计数,在计数到达0之前,线程会一直等待。开始之前,你需要先了解AQS的实现机制。

    CountDownLatch运行机制

    2. 数据结构和核心参数

    CountDownLatch继承关系
    从锁类别来说,CountDownLatch是一个“共享锁”,内部定义了自己的同步器Sync,Sync继承自AQS,实现了tryAcquireSharedtryReleaseShared两个方法。需要注意的是,CountDownLatch中的锁是响应中断的,如果线程在对锁进行操作期间发生中断,会直接抛出InterruptedException

    3. 使用示例

    public class CountDownLatchTest1 {
        private static int LATCH_SIZE = 5;
        private static CountDownLatch doneSignal;
        public static void main(String[] args) {
    
            try {
                doneSignal = new CountDownLatch(LATCH_SIZE);
    
                // 新建5个任务
                for(int i=0; i<LATCH_SIZE; i++)
                    new InnerThread().start();
    
                System.out.println("main await begin.");
                // "主线程"等待线程池中5个任务的完成
                doneSignal.await();
    
                System.out.println("main await finished.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    
        static class InnerThread extends Thread{
            public void run() {
                try {
                    System.out.println(Thread.currentThread().getName() + " sleep 1000ms.");
                    Thread.sleep(1000);
                    // 将CountDownLatch的数值减1
                    doneSignal.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    4. 源码解析

    //构造函数
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }
    //CountDownLatch中的计数其实就是AQS的state
    Sync(int count) {
        setState(count);
    }
    

    说明:从构造函数中可以看出,CountDownLatch的“锁计数”本质上就是AQS的资源数state。下面我们将通过await()countDown()两个方法来分析CountDownLatch的“latch”实现。

    4.1 await()

    public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    
    //AQS中acquireSharedInterruptibly(1)的实现
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
    
    //tryAcquireShared在CountDownLatch中的实现
    protected int tryAcquireShared(int acquires) {
        return (getState() == 0) ? 1 : -1;
    }
    

    说明:await()的实现非常简单,就是通过对资源state剩余量(state==0 ? 1 : -1)来判断是否获取到锁。在《AQS》篇中我们讲到过,tryAcquireShared函数规定了它的返回值类型:成功获取并且还有可用资源返回正数;成功获取但是没有可用资源时返回0;获取资源失败返回一个负数。 也就是说,只要state!=0,线程就进入等待队列阻塞。

    4.2 countDown()

    public void countDown() {
        sync.releaseShared(1);
    }
    
    //AQS中releaseShared(1)的实现
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();//唤醒后续节点
            return true;
        }
        return false;
    }
    
    //tryReleaseShared在CountDownLatch中的实现
    protected boolean tryReleaseShared(int releases) {
        // Decrement count; signal when transition to zero
        for (;;) {
            int c = getState();
            if (c == 0)
                return false;
            int nextc = c-1;
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }
    

    说明:如果释放资源后state==0,说明已经到达latch,此时就可以调用doReleaseShared唤醒等待的线程。

    小结

    相对其他同步类来说,CountDownLatch可以说是最简单的同步类实现了。它完全依赖了AQS,只要理解了AQS,那么理解它就不成问题了。

    相关文章

      网友评论

        本文标题:JUC源码分析-JUC锁(三):CountDownLatch

        本文链接:https://www.haomeiwen.com/subject/pdmzixtx.html