CountDownLatch
1. countDownLatch 简介
CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他countDownLatch中的线程里执行完后再执行其他等待的线程。
- 举例:要求主线程等待10个同样步骤的线程执行完后,主线程才继续执行下面的任务。任务开始时,就需要将10个线程放入计数器countDownLatch中,主线程等待,直到10个线程中每一个线程都执行完后,主线程才开始继续执行,在这个过程中,10个线程相互间可以不必等待,来一个就执行一个,直到计数器计算到有10个线程都执行完即可。
2.countDownLatch 的原理
通过一个计数器来实现的,计数器的初始化值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就相应得减1。当计数器到达0时,表示所有的线程都已完成任务,然后等待的线程就可以恢复执行任务。
- await用来等待,countDown负责计数器的减1;
- counDownLatch是一个一次性的, 计数无法重置。若需要一个重置的版本计数,考虑使用CyclicBarrier(下一篇文章介绍)。
结构图
上图,我们可以看到countDownLatch的主要方法,接下来为大家介绍主要使用的几个方法:
2.1 await()
函数将会使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。其源码如下:
public void await() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
//-----------------------------------------------------------
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//这里可以看到 最终调用的是Sync中的 tryAcquireShared 方法
// return (count == 0) ? 1 : -1;
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
//-------------------------------------------------------
public boolean await(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
2.2 .countDown()
此函数将递减(减一)锁存器的计数,如果计数到达零,则释放所有等待的线程
/**
* count值减 1,直到计数达到零,释放所有等待的线程 。
*
* <p>如果当前计数大于零,则递减。
* 如果新计数为零,则重新启用所有等待的线程 ,达到线程调度的目的。
*
* <p>如果当前计数等于零,则没有任何反应。
*/
public void countDown() {
sync.releaseShared(1);
}
//-------------------------------------------------------------------------
/**
* 此函数会以共享模式释放对象,
* 并且在函数中会调用到CountDownLatch的tryReleaseShared函数,
* 当且仅当新计数返回0时,会调用AQS的doReleaseShared函数,
*/
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
3.实例
初始化计数器要有6个线程执行完后,扣除完毕以后,主线程和业务线程才能继续自己的工作
代码如下:
public class UseCountDownLatch {
static CountDownLatch latch = new CountDownLatch(6);
//初始化线程(只有一步,有4个)
private static class InitThread implements Runnable{
@Override
public void run() {
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work......");
latch.countDown();//初始化线程完成工作了,countDown方法只扣减一次;
for(int i =0;i<2;i++) {
System.out.println("Thread_"+Thread.currentThread().getId()
+" ........continue do its work");
}
}
}
//业务线程
private static class BusiThread implements Runnable{
@Override
public void run() {
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
for(int i =0;i<3;i++) {
System.out.println("BusiThread_"+Thread.currentThread().getId()
+" do business-----");
}
}
}
public static void main(String[] args) throws InterruptedException {
//单独的初始化线程,初始化分为2步,需要扣减两次
new Thread(new Runnable() {
@Override
public void run() {
SleepTools.ms(1);
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work step 1st......");
latch.countDown();//每完成一步初始化工作,扣减一次
System.out.println("begin step 2nd.......");
SleepTools.ms(1);
System.out.println("Thread_"+Thread.currentThread().getId()
+" ready init work step 2nd......");
latch.countDown();//每完成一步初始化工作,扣减一次
}
}).start();
new Thread(new BusiThread()).start();
for(int i=0;i<=3;i++){
Thread thread = new Thread(new InitThread());
thread.start();
}
latch.await();
System.out.println("Main do ites work........");
}
}
运行结果如下:
Thread_13 ready init work......
Thread_13 ........continue do its work
Thread_13 ........continue do its work
Thread_14 ready init work......
Thread_14 ........continue do its work
Thread_14 ........continue do its work
Thread_15 ready init work......
Thread_15 ........continue do its work
Thread_15 ........continue do its work
Thread_11 ready init work step 1st......
begin step 2nd.......
Thread_16 ready init work......
Thread_16 ........continue do its work
Thread_16 ........continue do its work
Thread_11 ready init work step 2nd......
Main do ites work........
BusiThread_12 do business-----
BusiThread_12 do business-----
BusiThread_12 do business-----
版权声明:本文为qq_32445069原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。