它是一个同步工具类,允许一个或多个线程一直等待,直到其他线程运行完成后再执行。
- 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。
//构造函数参数来设置计数
CountDownLatch countDownLatch = new CountDownLatch(5);
下面看下简单的示例,代码如下:
MyThread.java继承Thread类
package com.shenlong.concurrent;
import java.util.concurrent.CountDownLatch;
public class MyThread extends Thread{
private final CountDownLatch countDownLatch;
//private final Random random = new Random();
private Integer num;
public MyThread(String name,Integer num,CountDownLatch countDownLatch){
super(name);
this.countDownLatch=countDownLatch;
this.num= num;
}
@Override
public void run(){
try {
Thread.sleep(1000);
long count = countDownLatch.getCount();
if(count>num){
return;
}else{
System.out.println(Thread.currentThread().getName()+"抢到了!");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
//将当前线程计数减1
countDownLatch.countDown();
}
}
在每个线程(任务) 完成的最后一行加上CountDownLatch.countDown(),让计数器-1;
当所有线程完成-1,计数器减到0后,主线程往下执行汇总任务。
Main类通过main方法调用MyThread类
package com.shenlong.concurrent;
import java.util.concurrent.CountDownLatch;
public class Main {
private static final Integer THREAD_NUM=3;
private static final Integer NUM=5;
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);
int j = 0;
for (int i = 0; i < NUM; i++) {
j++;
new MyThread("线程"+(i+1),j,countDownLatch).start();
}
//main线程等待
countDownLatch.await();
System.out.println("main线程执行完毕");
}
}
我们通过CountDownLatch.await(),让多个参与者线程启动后阻塞等待,然后在主线程 调用CountDownLatch.countdown(1) 将计数减为0,让所有线程一起往下执行;
这相当于是一个简单抢购操作,当前计数的数量是3,然后线程是有5个,然后进入到MyThread类里面进行抢购,执行run方法,当传入的 j<THREAD_NUM,则就可以抢购到东西。,结果输出的图片下:
但是这里一定要注意:循环的NUM一定要大于等于THREAD_NUM,即NUM>=THREAD_NUM,要不然线程会阻塞。这里只是稍微模拟了一下抢购,实际抢购不是这样的,需要用到信号亮来操作的。这里就不多介绍了。
如下所示,await()调用的是AQS 的模板方法,这个方法在前面已经介绍过。 CountDownLatch.Sync重新实现了tryAccuqireShared方法:
public void await() throws InterruptedException { // AQS的模板方法
sync.acquireSharedInterruptibly(1);
}
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException(); // 被CountDownLatch.Sync实现
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
从tryAcquireShared(...)方法的实现来看,只要state != 0,调用await()方法的线程便会被放入AQS
的阻塞队列,进入阻塞状态。
public void countDown() {
sync.releaseShared(1);
}
//AQS模板方法
public final boolean releaseShared(int arg) {
// 由CountDownLatch.Sync实现
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
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;
}
}
countDown()调用的AQS的模板方法releaseShared(),里面的tryReleaseShared(...)由CountDownLatch.Sync实现。从上面的代码可以看出,只有state=0,tryReleaseShared(...)才会返回true,然后执行doReleaseShared(...),一次性唤醒队列中所有阻塞的线程。
总结:由于是基于AQS阻塞队列来实现的,所以可以让多个线程都阻塞在state=0条件上,通过countDown()一直减state,减到0后一次性唤醒所有线程。如下图所示,假设初始总数为M,N个线程await(),M个线程countDown(),减到0之后,N个线程被唤醒。