CountDownLatch源码简要分析


一、前言

  前面已经通过小例子介绍了CountDownLatch的基本使用场景,在本篇博客中我们简要的看下CountDownLatch的源码,看看是如何实现的,前提是必须先要知道AQS的大体流程,因为CountDownLatch是基于AQS实现的,不清楚的可以先看《AbstractQueuedSynchronizer源码——基本认识》

二、源码

  整体上CountDownLatch的源码很少,一个内部类外加几个方法,下面我们就以之前用到的几个方法来看看。

	public class CountDownLatch {
	
	    private static final class Sync extends AbstractQueuedSynchronizer {...}
	
	    private final Sync sync;
	
	    public CountDownLatch(int count) {...}
	
	    public void await() throws InterruptedException {...}
	
	    public boolean await(long timeout, TimeUnit unit) throws InterruptedException {...}
	    
	    public void countDown() {...}
	
		public long getCount() {...}
		
	    public String toString() {...}
	}

2.1 构造方法

  构造方法里面我们可以看到,传入一个参数创建了一个Sync对象,那个这个Sync就是CountDownLatch的内部类。

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

2.2 Sync类

  Sync继承了AQS,并且重写了AQS中的tryAcquireShared、tryReleaseShared方法,这个之前介绍AQS时就说过,这两个是AQS子类自行设计逻辑。

    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

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

        int getCount() {
            return getState();
        }

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

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

  CountDownLatch构造方法中调用了Sync的构造方法,Sync中又调用了AQS的setState方法,这里可以看到最初创建CountDownLatch对象时传入的参数是赋值给了AQS中的state。

	// #AbstractQueuedSynchronizer
    protected final void setState(int newState) {
        state = newState;
    }

2.3 await

  在之前的例子中,我们在需要等待的地方调用了await方法,await中调用了AQS中的acquireSharedInterruptibly,此方法会相应中断。

    public void await() throws InterruptedException {
    	// acquireSharedInterruptibly跟acquireShared逻辑基本一致,区别就是acquireSharedInterruptibly是响应中断的
        sync.acquireSharedInterruptibly(1);
    }

  在acquireSharedInterruptibly中会调用tryAcquireShared尝试获取资源,那么这个tryAcquireShared方法就是上面提到过的,在CountDownLatch中的内部类Sync中重写的。

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

  可以看到,如果当前的state=0,那就会返回1,否则返回-1。

	// #CountDownLatch.Sync
	protected int tryAcquireShared(int acquires) {
	    return (getState() == 0) ? 1 : -1;
	}

  所以,综合上面三个方法的调用关系,如果执行await时计数不为0,那么就会在acquireSharedInterruptibly方法中执行doAcquireSharedInterruptibly方法,当前线程就会进入等待队列,并且转为阻塞状态等待唤醒具体过程在《AbstractQueuedSynchronizer源码——基本认识》有说明),这就是例子中某个线程调用了await后会出现等待的原因。

2.4 countDown

  在例子中,子线程完成逻辑后都会调用countDown,countDown方法会让当前技术减1。

    public void countDown() {
    	// AbstractQueuedSynchronizer的方法
        sync.releaseShared(1);
    }
	//# AbstractQueuedSynchronizer
    public final boolean releaseShared(int arg) {
    	// 这里的tryReleaseShared也是在Sync中重写了
        if (tryReleaseShared(arg)) {
        	// 唤醒下一个线程或者设置传播状态
            doReleaseShared();
            return true;
        }
        return false;
    }
	// #CountDownLatch.Sync
    protected boolean tryReleaseShared(int releases) {
        // 自旋,减1
        for (;;) {
            int c = getState();
            // state等于0,结束返回false
            if (c == 0)
                return false;
            // state不等于0时,state减1
            int nextc = c-1;
            // 并通过CAS操作确保state设置成功,否则再次循环知道设置成功,然后跳出循环返回判断结果
            if (compareAndSetState(c, nextc))
                return nextc == 0;
        }
    }

  通过tryReleaseShared方法的返回值,然后根据判断是否在releaseShared中调用doReleaseShared方法,该方法就是用来唤醒下一个线程或者设置传播状态的(具体过程在《AbstractQueuedSynchronizer源码——基本认识》有说明),所以可以看到在例子中当某个线程执行countDown方法后,若计数减1刚好等于0,那么就会触发执行唤醒线程的方法,从而使得此前调用await方法而进入阻塞状态的线程被唤醒继续执行

三、小结

  上面就是关于CountDownLatch的基本介绍,总体而言逻辑并不复杂,但是前提是必选先弄清楚AQS的大体流程。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值