美文网首页
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