欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

浅谈java并发之计数器CountDownLatch

程序员文章站 2022-07-20 19:02:45
countdownlatch简介 countdownlatch顾名思义,count + down + latch = 计数 + 减 + 门闩(这么拆分也是便于记忆=_=)...

countdownlatch简介

countdownlatch顾名思义,count + down + latch = 计数 + 减 + 门闩(这么拆分也是便于记忆=_=) 可以理解这个东西就是个计数器,只能减不能加,同时它还有个门闩的作用,当计数器不为0时,门闩是锁着的;当计数器减到0时,门闩就打开了。

如果你感到懵比的话,可以类比考生考试交卷,考生交一份试卷,计数器就减一。直到考生都交了试卷(计数器为0),监考老师(一个或多个)才能离开考场。至于考生是否做完试卷,监考老师并不关注。只要都交了试卷,他就可以做接下来的工作了。

countdownlatch实现原理

下面从构造方法开始,一步步解释实现的原理:构造方法下面是实现的源码,非常简短,主要是创建了一个sync对象。

public countdownlatch(int count) {
if (count < 0) throw new illegalargumentexception("count < 0");
this.sync = new sync(count);
}

sync对象

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

假设我们是这样创建的:new countdownlatch(5)。其实也就相当于new sync(5),相当于setstate(5)。setstate其实就是共享锁资源总数,我们可以暂时理解为设置一个计数器,当前计数器初始值为5。

tryacquireshared方法其实就是判断一下当前计数器的值,是否为0了,如果为0的话返回1(返回1的时候,就表示获取锁成功,awit()方法就不再阻塞)。

tryreleaseshared方法就是利用cas的方式,对计数器进行减一的操作,而我们实际上每次调用countdownlatch.countdown()方法的时候,最终都会调到这个方法,对计数器进行减一操作,一直减到0为止。

countdownlatch.await()

public void await() throws interruptedexception { 
sync.acquiresharedinterruptibly(1); 
}

代码很简单,就一句话(注意acquiresharedinterruptibly()方法是抽象类:abstractqueuedsynchronizer的一个方法,我们上面提到的sync继承了它),我们跟踪源码,继续往下看:

acquiresharedinterruptibly(int arg) 
public final void acquiresharedinterruptibly(int arg)
throws interruptedexception {
if (thread.interrupted())
throw new interruptedexception();
if (tryacquireshared(arg) < 0)
doacquiresharedinterruptibly(arg);
}

源码也是非常简单的,首先判断了一下,当前线程是否有被中断,如果没有的话,就调用tryacquireshared(int acquires)方法,判断一下当前线程是否还需要“阻塞”。其实这里调用的tryacquireshared方法,就是我们上面提到的java.util.concurrent.countdownlatch.sync.tryacquireshared(int)这个方法。

当然,在一开始我们没有调用过countdownlatch.countdown()方法时,这里tryacquireshared方法肯定是会返回-1的,因为会进入到doacquiresharedinterruptibly方法。 

doacquiresharedinterruptibly(int arg)

浅谈java并发之计数器CountDownLatch

countdown()方法

// 计数器减1
public void countdown() {
sync.releaseshared(1); 
}
//调用aqs的releaseshared方法
public final boolean releaseshared(int arg) {
if (tryreleaseshared(arg)) {//计数器减一
doreleaseshared();//唤醒后继结点,这个时候队列中可能只有调用过await()的线程节点,也可能队列为空
return true;
}
return false;
}

这个时候,我们应该对于countdownlatch.await()方法是怎么“阻塞”当前线程的,已经非常明白了。其实说白了,就是当你调用了countdownlatch.await()方法后,你当前线程就会进入了一个死循环当中,在这个死循环里面,会不断的进行判断,通过调用tryacquireshared方法,不断判断我们上面说的那个计数器,看看它的值是否为0了(为0的时候,其实就是我们调用了足够多 countdownlatch.countdown()方法的时候),如果是为0的话,tryacquireshared就会返回1,代码也会进入到图中的红框部分,然后跳出了循环,也就不再“阻塞”当前线程了。

需要注意的是,说是在不停的循环,其实也并非在不停的执行for循环里面的内容,因为在后面调用parkandcheckinterrupt()方法时,在这个方法里面是会调用 locksupport.park(this);来挂起当前线程。

countdownlatch 使用的注意点:

1、只有当count为0时,await之后的程序才够执行。

2、countdown必须写在finally中,防止发生异程常时,导致程序死锁。

使用场景:

比如对于马拉松比赛,进行排名计算,参赛者的排名,肯定是跑完比赛之后,进行计算得出的,翻译成java识别的预发,就是n个线程执行操作,主线程等到n个子线程执行完毕之后,在继续往下执行。

public static void testcountdownlatch(){
int threadcount = 10;
final countdownlatch latch = new countdownlatch(threadcount);
for(int i=0; i< threadcount; i++){
new thread(new runnable() {
@override
public void run() {
system.out.println("线程" + thread.currentthread().getid() + "开始出发");
try {
thread.sleep(1000);
system.out.println("线程" + thread.currentthread().getid() + "已到达终点");
} catch (interruptedexception e) {
e.printstacktrace();
} fianlly {
latch.countdown();
}
}
}).start();
}
try {
latch.await();
} catch (interruptedexception e) {
e.printstacktrace();
}
system.out.println("10个线程已经执行完毕!开始计算排名");
}

结果:

线程10开始出发
线程13开始出发
线程12开始出发
线程11开始出发
线程14开始出发
线程15开始出发
线程16开始出发
线程17开始出发
线程18开始出发
线程19开始出发
线程14已到达终点
线程15已到达终点
线程13已到达终点
线程12已到达终点
线程10已到达终点
线程11已到达终点
线程16已到达终点
线程17已到达终点
线程18已到达终点
线程19已到达终点
10个线程已经执行完毕!开始计算排名

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。