美文网首页
JAVA-Lock解析-六-CountDownLatch解析

JAVA-Lock解析-六-CountDownLatch解析

作者: AlanSun2 | 来源:发表于2019-10-14 18:13 被阅读0次

    CountDownLatch 的作用是允许一个或多个线程进入阻塞,直到 countDown 把 state 的值减到 0 才去唤醒线程。

    CountDownLatch 本质上使用共享锁,这点体现在释放锁时使用了 AQS#releaseShared 方法。这使得 CountDownLatch 在释放时,能够把同步队列中的线程逐一全部唤醒。

    CountDownLatch 有一个静态内部类 Sync 继承自 AbstractQueuedSynchronizer。

    public class CountDownLatch {
    
        private static final class Sync extends AbstractQueuedSynchronizer {
            private static final long serialVersionUID = 4982264981922014374L;
            
            //构造方法中把 state 设置为指定的值
            Sync(int count) {
                setState(count);
            }
    
            int getCount() {
                return getState();
            }
            
            //调用链:
            //CountDownLatch#await/await(time) -> AQSacquireSharedInterruptibly/tryAcquireSharedNanos -> tryAcquireShared(本方法)
            //只要 state != 0,都返回 -1,表示阻塞线程,详情请看 JAVA-Lock解析-三。
            //只有当 state == 0 时,才会返回 1,表示线程退出等待并可以唤醒下一个 Node。
            //这也证明了当执行 await 时如果 state == 0,为什么会直接返回,不会阻塞。
            protected int tryAcquireShared(int acquires) {
                return (getState() == 0) ? 1 : -1;
            }
            
            //调用链:
            // CountDownLatch#countDown ->AQS#releaseShared -> tryReleaseShared(本方法)
            //本质上就是把 state - 1,但只有减到 0 才会返回 true,进入唤醒模式。否则不做任何操作。
            //详情请看 JAVA-Lock解析-三。
            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;
                }
            }
        }
    
        private final Sync sync;
    
        //count:表示在唤醒阻塞线程前需要调用 countDown 方法的次数
        public CountDownLatch(int count) {
            if (count < 0) throw new IllegalArgumentException("count < 0");
            this.sync = new Sync(count);
        }
        
        //只要不被中断,线程会阻塞直至 state 减至 0。
        //如果 state 已经是 0,则立刻返回,不会阻塞。
        public void await() throws InterruptedException {
            sync.acquireSharedInterruptibly(1);
        }
        
        //线程会阻塞直至 state 减至 0,除非被中断或超时
        //如果 state 已经是 0,则立刻返回,不会阻塞。
        public boolean await(long timeout, TimeUnit unit)
            throws InterruptedException {
            return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
        }
        
        //使 state -1,如过 state = 0,则开始唤醒队列中阻塞的线程
        public void countDown() {
            sync.releaseShared(1);
        }
        
        //获取当前的 state 值
        public long getCount() {
            return sync.getCount();
        }
    
        public String toString() {
            return super.toString() + "[Count = " + sync.getCount() + "]";
        }
    }
    

    总结

    可以看到 CountDownLatch 还是非常简单的,但前提是你对 AQS 非常熟悉。另外官方文章还提到了 CountDownLatch 一种特殊情况的替代 CyclicBarrier (内部使用的 ReentrantLock),有兴趣的可以自己看下。希望对大家有帮助,如有错误请指正。

    相关文章

      网友评论

          本文标题:JAVA-Lock解析-六-CountDownLatch解析

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