CountDownLatch源码简析

CountDownLatch把这个单词拆分成两部分,CountDown是倒计时的意思,Latch的意思是门闩,也就是锁的意思,所以CountDownLatch其实是一个倒计时锁,当倒计时不为0的时候,就会被锁阻塞,当倒计时为0时,锁会被释放,继续执行逻辑

先来一段demo代码

public class CountdownTest {
    public static void main(String[] args){
        CountDownLatch latch = new CountDownLatch(2);
        System.out.println("main线程开始执行");

        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程1 countDown");
                latch.countDown();
            }
        }.start();

        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程2 countDown");
                latch.countDown();
            }
        }.start();

        try {
            System.out.println("被countDownLatch阻塞");
            latch.await();
            System.out.println("main线程继续执行");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

输出

main线程开始执行
被countDownLatch阻塞
线程1 countDown
线程2 countDown
main线程继续执行

从上面的demo中我们可以看到三个关键的地方,我们一个个来分析一下,首先是CountDownLatch的构造方法

CountDownLatch latch = new CountDownLatch(2);

这里传入的参数就是倒计时的次数,来看看源码

public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    // Sync(int count) {
    //     setState(count);
    // }
    // 传入的参数,最后赋值给了state变量,这是AQS中的变量,AQS是并发包的基础
    // state在AQS中就代表了重入加锁的次数,可以认为是加了count数量的锁
    this.sync = new Sync(count);
}

接下来分析一下CountDownLatch的await方法

public void await() throws InterruptedException {
	// 参数传入1
    sync.acquireSharedInterruptibly(1);
}

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    // 判断state是否等于0,如果等于则返回1,否则返回-1
    if (tryAcquireShared(arg) < 0)
    	// 这里的代码涉及到了AQS中的Node链表的逻辑,所以不展开细讲
    	// 大意是如果state不等于0,说明现在倒计时未结束,通过LockSupport.park()操作将当前线程挂起
        doAcquireSharedInterruptibly(arg);
}

CountDownLatch的await方法在计数器没有到0的时候,就会将线程挂起

最后是CountDownLatch的countDown方法

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

public final boolean releaseShared(int arg) {
	// 这一步通过CAS操作将state减1,如果state减为了0,才返回true
    if (tryReleaseShared(arg)) {
    	// 涉及到了AQS中的Node链表逻辑,不展开细讲
    	// 这一步其实就是唤醒了被挂起的线程,让所有被CountDownLatch阻塞住的线程都继续往下执行
        doReleaseShared();
        return true;
    }
    return false;
}

讲完了CountDownLatch的构造方法,await方法和countDown方法,大家对CountDownLatch基本也就理解了

这里分析一下demo代码的运行流程,首先进入main方法,输出 main线程开始执行 ,之后开启了两个线程,都sleep了一秒,此时程序运行到await方法,发现此时计数器还不为0,所以输出 被countDownLatch阻塞 ,然后线程1和线程2都执行countDown方法,输出 线程1 countDown 和 线程2 countDown,此时计数器已经被减为0了,main线程继续执行,输出 main线程继续执行

CountDownLatch其实也很简单,构造方法就是设置倒计时的数,countDown就是进行倒计时,await方法就是如果倒计时还没结束的话,就挂起线程,等到倒计时结束后,将挂起的线程唤醒,继续执行之后的逻辑

你可能感兴趣的:(并发编程)