摘要:介绍Java中CountDownLatch的使用方法,重点包括计数器的值、countDown和await。计数器的值表示任务线程的个数,每次countDown都会使计数减一,减到0的时候调用await方法的线程就不再被阻塞。
综述
CountDownLatch一般被称作"计数器",计数器的初始值是线程的数量,是一个同步工具类,用来协调多个线程之间的同步。其中,count down是倒数的意思,latch则是门闩的含义,整体含义可以理解为倒数的门栓;有一点“三二一,芝麻开门”的感觉。
CountDownLatch在初始化时,需要指定一个正整数作为计数器起始值,每当一个线程执行完毕后,计数器就被countDown方法减1,当计数器为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就被唤醒,恢复工作。从线程执行状态的角度分析,就是允许一个或多个线程等待,直到其它线程执行完成才继续往下走。
CountDownLatch是在java 1.5 被引入的,与它一起被引入的工具类还包括CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue等,它们都位于java.util.cucurrent包下。
CountDownLatch的计数器无法被重置,当计数器被减到0时,调用await方法会直接返回。这一点有别于Semaphore,Semaphore是可以通过release操作恢复信号量的。
CountDownLatch类
CountDownLatch 类提供了三个核心构造器:
//参数count为计数值,即线程个数
public CountDownLatch(int count) { };
/**
* Causes the current thread to wait until the latch has counted down to
* zero, unless the thread is {@linkplain Thread#interrupt interrupted}.
*
* <p>If the current count is zero then this method returns immediately.
*
* 调用await()方法的线程会被挂起,直到count值为0才继续执行
*/
public void await() throws InterruptedException { };
//和await()类似,只不过等待一定的时间后,如果计数器的值还没倒数至0,就会被唤醒继续执行
public boolean await(long timeout, TimeUnit unit) throws InterruptedException { };
/**
* Decrements the count of the latch, releasing all waiting threads if
* the count reaches zero.
* 每次减少一个门闩数,如果等于零,则释放等待线程
* <p>If the current count is greater than zero then it is decremented.
* If the new count is zero then all waiting threads are re-enabled for
* thread scheduling purposes.
*
* <p>If the current count equals zero then nothing happens.
*/
public void countDown() { };
如果想使线程处于等待状态,则调用await()方法,此线程一般是主线程。温馨提示,await()方法并没有规定只能由一个线程执行,如果多个线程同时执行await()方法,那么这几个线程都将处于等待状态,并且以共享模式享有同一把锁。另外,当同一个线程多次调用countDown()方法时,每次都会使计数器减一;
简单总结一下,CountDownLatch有三个重点:计数器的值、await、countDown。计数器的值表示任务线程的个数,每次countDown都会使计数减一,减到0的时候await方法所在的线程就不再被阻塞。
使用场景
CountDownLatch非常适合于对任务进行拆分,使其并行执行,实现最大的并行性。某一线程A在开始运行前一直被阻塞,需等待n个其它线程执行完毕,则需将CountDownLatch的计数器初始化为 new CountDownLatch(n)
,每当一个任务线程执行完毕,则调用countDown()函数使得计数器减一;当计数器变为0时,在CountDownLatch上await()的线程A就会被唤醒。
例1,会计汇总一年的财务报表时,可以把任务按照季度拆分为四份,那么就可以将这个大任务拆分为4个子任务,分别交由4个财务(线程)执行,执行完成之后再由主财务(主线程)进行财务汇总。此时,总的执行时间将决定于执行最慢的任务,通常来看,还是可以大大减少总的执行时间。
假2,有10个线程需要加载一些资源,而另外一个线程A必须在所有资源被加载完成后才能继续执行,那么我们可以new一个CountDownLatch(10)的闭锁,每个加载资源的线程执行完后都调用countDown()函数,而线程A则在需要的资源准备齐全之前因调用await函数而被阻塞,当计数器的值为0时,线程A就会被唤醒继续执行。
知行合一
案例请戳《Java 使用线程池和CountDownLatch分批插入或者更新数据》。
结束语
JUC CountDownLatch是一个同步工具类,用来协调多个线程之间的同步。这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再开始执行。原理:它通过一个计数器来实现的,计数器的初始化值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就减一。当计数器递减至零时,表示所有的线程都已完成任务,然后在闭锁上等待的线程就被唤醒,恢复执行任务。
老铁们, 因个人能力有限,难免有瑕疵,如果发现bug或者有更好的建议,那么请在文章下方留言!