CountDownLatch怎么用?让你分分钟搞懂

CountDownLatch

最近对各种锁啊线程啊希望有更深入了解,什么乐观锁、悲观锁etc都去从理论实际理解了一些,然后这俩天做leetcode时做了个线程遍历的题。

这是原题链接线程按序遍历

题目中可以写个全局flag通过while(true)或者for(;;)来等待前面线程方法的完成,也可以使用信号量,然后我想起之前使用过的CountDownLatch,发现实现起来也很简单易读。

先贴个使用实例吧

class Foo {
	//获取俩个CountDownLatch实例
    CountDownLatch countDownLatch2 = new CountDownLatch(1);
    CountDownLatch countDownLatch3 = new CountDownLatch(1);
    public Foo() {}

    public void first(Runnable printFirst) throws InterruptedException {
        // printFirst.run() outputs "first". 
        printFirst.run();
        //first()运行完后将countDownLatch2减少
        countDownLatch2.countDown();
    }

    public void second(Runnable printSecond) throws InterruptedException {
        countDownLatch2.await();
        // printFirst.run() outputs "second".
        printSecond.run();
        //first()运行完后将countDownLatch3减少
        countDownLatch3.countDown();
    }

    public void third(Runnable printThird) throws InterruptedException {
        countDownLatch3.await();
        // printThird.run() outputs "third". 
        printThird.run();
    }
}

在这里面是最简单的countDownLatch的使用,简单来理解就是使用constructor
CountDownLatch(int count)来获取CountDownLatch对象
这里的count是需要>=0的,否则会抛出exception

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

然后是将全局的sync初始化,Sync是一个静态类,它的初始化方法是调用了一个set方法

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

我们继续跟进,setState()方法仅仅是将全局可见变量state赋值,所以我们对整个CountDownLatch的初始化,实际上是对state进行赋值而已,就是这么一个简单的操作。
简而言之语句:

CountDownLatch countDownLatch = new CountDownLatch(1);

相当于初始化了一个state属性为1的CountDownLatch对象。
明白了初始化的作用之后,我们再来看await()方法和countDown()方法

秒一下countDown()方法

先来看countDown()方法,其本身的表层含义是非常明确的,其实就是将当前CountDownLatch的state属性进行减一操作,底层源码是这么进行的:

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

首先其实该方法是调用了Sync继承的AbstractQueuedSynchronizer(也就是通常所说的AQS)类的releaseShared(int arg)方法:

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

Sync类对其tryReleaseShared()进行了overridestryReleaseShared(int releases)

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

我们可以看到实际上源码是做了一个循环,我们可以注意一下,理论上while(true)for(;;)是一样的效果,但是在计算机底层编译实现的时候,for()的指令会比while()更少,所以效率更高,基于此,大多数时候底层源码都是使用的for(;;)而不是while(true)
这个函数其实也很好看懂,就是不断获取state值并判断其是否为0,如果不为0就将其减一,减一的时候进行的CAS操作。

CAS操作这里就不跟进了,简而言之就是:带入一个更改的地址,二是希望这个地址属性应该是什么值,三是需要修改成什么值,如果期望值不匹配就不进行修改。

概况来说,countDown()方法就是通过原子性的CAS操作将state属性减一。

最后来看一下await()方法

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

源码中是调用了Sync继承自AQS的acquireSharedInterruptibly(int arg)方法,我们跟进,可以看到方法体里面首先是对线程是否中断进行了一个判断,如果线程没问题就去尝试查看state是否为0,如果为0,条件不通过,不为0条件通过

public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
	if (Thread.interrupted())
		throw new InterruptedException();
	if (tryAcquireShared(arg) < 0)
		doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
	return (getState() == 0) ? 1 : -1;
}

不为0的情况下,其会进去调用doAcquireSharedInterruptibly(arg)方法

private void doAcquireSharedInterruptibly(int arg) throws InterruptedException {
        final Node node = addWaiter(Node.SHARED);
        try {
        	//自旋
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } catch (Throwable t) {
            cancelAcquire(node);
            throw t;
        }
    }

该方法主要进行了一个自旋操作,尝试去挂起当前线程,这样就是是如果当前CountDownLatch类的state不为0,便会将当前线程挂起并等待state被更改为0后再跳出await()方法

怎么样,看完后是不是发现CountDownLatch其实很好用,初始化,countDown,await的代码逻辑都是非常清晰的,实现了一个等待其他线程将state减少世道当前线程跳出await状态的过程~

如果对你有帮助的话,建议点个赞噢QAQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值