09、JUC源码分析:CountDownLatch源码分析

JUC-AQS原理篇
JUC-AQS源码篇
JUC-AQS的Condition之await和signal源码解析

JUC-CountDownLatch基础篇
JUC-CountDownLatch源码分析
JUC-Semaphore基础篇
JUC-Semaphore源码分析
JUC-ReentrantReadWriteLock锁基础篇
JUC-ReentrantReadWriteLock锁源码分析
JUC-ReentrantLock锁基础篇
JUC-ReentrantLock锁源码分析
JUC-CyclicBarrier基础篇
JUC-CyclicBarrier源码分析

文章目录

    1. CountDownLatch类结构图
    • 1.1 CountDownLatch的构造方法
    1. await()方法源码
    • 2.1. doAcquireShared(int arg)方法
    1. countDown()方法源码
    • 3.1 tryReleaseShared方法源码

建议阅读CountDownLatch源码分析之前, 先阅读JUC-CountDownLatch基础篇,了解一下CountDownLatch用处以及基本用法。

我们知道CountDownLatch是一个同步工具类,表示允许一个或者多个线程来等待其他的线程来完成它们的操作后,才继续执行自己的操作。

1. CountDownLatch类结构图

*
CountDownLatch的类结构图很简单,内部就一个继承了AbstractQueuedSynchronizer的静态抽象类Sync。

1.1 CountDownLatch的构造方法

 public CountDownLatch(int count) {
   
     
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

此构造方法就是传递一个非负数的count值,来设置计时器的初始值。以后每调用一次countDown方法,计时器的计数值就会减一,知道计时器的计数值为0了,就会唤醒因为调用await方法而阻塞的线程了。

Sync类的构造方法

 Sync(int count) {
   
     
            setState(count);
        }

通过这个方法我们知道AQS中的state值,此时在CountDownLatch中就是代表了一个计时器的计数值。

2. await()方法源码

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

直接调用AQS的acquireSharedInterruptibly方法

 public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
   
     
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

此方法中如果tryAcquireShared方法的返回值小于0,代表计时器的计数值还没有归零,则就调用AQS的doAcquireSharedInterruptibly方法将当前线程加入到同步队列中并且阻塞住(doAcquireSharedInterruptibly源码实现可以参考AQS源码篇中doAcquireShared方法)
而tryAcquireShared方法在AQS中是没有实现的,由CountDownLatch中继承了AQS类的内部类Sync提供了实现。

2.1. doAcquireShared(int arg)方法

 protected int tryAcquireShared(int acquires) {
   
     
            return (getState() == 0) ? 1 : -1;
        }

此方法实现很简单,就是判断state的值是不是0,如果是0的话,就代表计时器的计数值归零了,那么当前线程就不用阻塞了,返回1。如果不是0的话,就代表计时器的计数值还没有归零,那么当前线程需要阻塞了,返回-1。

3. countDown()方法源码

public void countDown() {
   
     
        sync.releaseShared(1);
    }

直接调用AQS的releaseShared方法

 public final boolean releaseShared(int arg) {
   
     
        if (tryReleaseShared(arg)) {
   
     
            doReleaseShared();
            return true;
        }
        return false;
    }

如果tryReleaseShared方法的返回值为true,代表已经将计时器的计数值归零了,则就调用AQS的doReleaseShared方法将那些因为调用了await()方法而阻塞住的线程唤醒,告诉它们,计时器的计数值归零了。这里就体现出来了CountDownLatch的用法,一个线程需要等待其他线程都操作完,才能接着干自己的事情。这里tryReleaseShared方法返回true,就表示了其他线程都操作完,这个线程可以接着干自己的事情了(doReleaseShared方法的源码可以参考AQS源码篇
而tryReleaseShared方法在AQS中是没有实现的,由CountDownLatch中继承了AQS类的内部类Sync提供了实现。

3.1 tryReleaseShared方法源码

 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;
            }
        }

此方法大致流程就是:

1、 开始一个死循环;
2、 获取计时器的计数值,赋值给c变量;
3、 如果c为0,代表计时器的计数值早就归零了,那么就不需要当前线程多此一举,去唤醒了,直接返回false;
4、 如果c不为0,就将c减一赋值给nextc变量并通过CAS操作将计时器的计数值更新为nextc变量值;
5、 CAS操作成功了,就将nextc变量与0相比较;
6、 如果nextc变量等于0,就说明了计时器的计数值已经归零了,返回true,唤醒因调用await返回阻塞的线程如果不等于0,就说明计时器的计数值还没有归零,直接返回false;