JUC下工具类CountDownLatch用法以及源码理解
程序员文章站
2022-03-03 10:40:05
CountDownLoatch是JUC下一个用于控制计数的计数器,比如我需要从6开始计数,每个线成运行完之后计数减一,等计数器到0时候开始执行其他任务。 public static void main(String[] args) throws InterruptedException { Coun ......
countdownloatch是juc下一个用于控制计数的计数器,比如我需要从6开始计数,每个线成运行完之后计数减一,等计数器到0时候开始执行其他任务。
public static void main(string[] args) throws interruptedexception { countdownlatch countdownlatch = new countdownlatch(6); for (int i = 0; i < 6; i++) { new thread(() -> { system.out.println(thread.currentthread().getname() + "\t结束"); countdownlatch.countdown(); }, string.valueof(i)).start(); } countdownlatch.await(); system.out.println("开始执行新任务"); }
现在让我们来看下countdownloach的底层源码
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; } } } private final sync sync;
这里最重要的是这个tyrreleaseshared方法,countdownlatch也是实现了aqs机制,通过state去判断资源知否被占用。当我们使用countdow函数去减1时,会触发releaseshared方法,这个方法的参数时每次减的次数,默认是1
public void countdown() {
sync.releaseshared(1);
}
我们点进去看看这个方法是如何实现的
public final boolean releaseshared(int arg) {
if (tryreleaseshared(arg)) {
doreleaseshared();
return true;
}
return false;
}
这个方法调用了aqs的releaseshared,他会先去尝试减一,判断是否减一成功
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;
}
}
这里使用了cas自旋机制对state(我们这里是6)进行减1,如果自旋成功就会返回true即6一个一个减最后变成0,就会执行下面的doreleaseshared方法
private void doreleaseshared() {
for (;;) {
node h = head;
if (h != null && h != tail) {
int ws = h.waitstatus;
if (ws == node.signal) {
if (!compareandsetwaitstatus(h, node.signal, 0))
continue; // loop to recheck cases
unparksuccessor(h);
}
else if (ws == 0 &&
!compareandsetwaitstatus(h, 0, node.propagate))
continue; // loop on failed cas
}
if (h == head) // loop if head changed
break;
}
}
这里使用了locksupport相关的加锁解锁,unpark有关这个我会在之后有空的时候进行解析,通过对aqs任务队列进行判断,是否只有头节点和尾节点,这个任务队列里面头节点并不是真正的任务,而是为了初始化方便操作的哨兵节点,然后对第一个任务节点进行操作,就是我们这6步操作,每次减一作为一个任务进行释放(我自己认为的)。
这是我第一次发帖子希望大家多多支持,准备秋招了,争取大厂上岸,如果帖子里有错误希望大家积极提出来
上一篇: MYSQL基础语句(自我记忆)