详解Java多线程编程中CountDownLatch阻塞线程的方法
直译过来就是倒计数(countdown)门闩(latch)。倒计数不用说,门闩的意思顾名思义就是阻止前进。在这里就是指 countdownlatch.await() 方法在倒计数为0之前会阻塞当前线程。
countdownlatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
countdownlatch 的作用和 thread.join() 方法类似,可用于一组线程和另外一组线程的协作。例如,主线程在做一项工作之前需要一系列的准备工作,只有这些准备工作都完成,主线程才能继续它的工作。这些准备工作彼此独立,所以可以并发执行以提高速度。在这个场景下就可以使用 countdownlatch 协调线程之间的调度了。在直接创建线程的年代(java 5.0 之前),我们可以使用 thread.join()。在 juc 出现后,因为线程池中的线程不能直接被引用,所以就必须使用 countdownlatch 了。
countdownlatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countdown()方法,计数器减1,计数器大于0 时,await()方法会阻塞程序继续执行。 countdownlatch可以看作是一个倒计数的锁存器,当计数减至0时触发特定的事件。利用这种特性,可以让主线程等待子线程的结束。 下面以一个模拟运动员比赛的例子加以说明。
countdownlatch的一个非常典型的应用场景是:有一个任务想要往下执行,但必须要等到其他的任务执行完毕后才可以继续往下执行。假如我们这个想要继续往下执行的任务调用一个countdownlatch对象的await()方法,其他的任务执行完自己的任务后调用同一个countdownlatch对象上的countdown()方法,这个调用await()方法的任务将一直阻塞等待,直到这个countdownlatch对象的计数值减到0为止。
countdownlatch函数列表
countdownlatch(int count) 构造一个用给定计数初始化的 countdownlatch。 // 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。 void await() // 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。 boolean await(long timeout, timeunit unit) // 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。 void countdown() // 返回当前计数。 long getcount() // 返回标识此锁存器及其状态的字符串。 string tostring()
countdownlatch数据结构
countdownlatch的uml类图如下:
countdownlatch的数据结构很简单,它是通过"共享锁"实现的。它包含了sync对象,sync是sync类型。sync是实例类,它继承于aqs。
countdownlatch的使用示例
下面通过countdownlatch实现:"主线程"等待"5个子线程"全部都完成"指定的工作(休眠1000ms)"之后,再继续运行。
import java.util.concurrent.countdownlatch; import java.util.concurrent.cyclicbarrier; public class countdownlatchtest1 { private static int latch_size = 5; private static countdownlatch donesignal; public static void main(string[] args) { try { donesignal = new countdownlatch(latch_size); // 新建5个任务 for(int i=0; i<latch_size; i++) new innerthread().start(); system.out.println("main await begin."); // "主线程"等待线程池中5个任务的完成 donesignal.await(); system.out.println("main await finished."); } catch (interruptedexception e) { e.printstacktrace(); } } static class innerthread extends thread{ public void run() { try { thread.sleep(1000); system.out.println(thread.currentthread().getname() + " sleep 1000ms."); // 将countdownlatch的数值减1 donesignal.countdown(); } catch (interruptedexception e) { e.printstacktrace(); } } } }
运行结果:
main await begin. thread-0 sleep 1000ms. thread-2 sleep 1000ms. thread-1 sleep 1000ms. thread-4 sleep 1000ms. thread-3 sleep 1000ms. main await finished.
结果说明:主线程通过donesignal.await()等待其它线程将donesignal递减至0。其它的5个innerthread线程,每一个都通过donesignal.countdown()将donesignal的值减1;当donesignal为0时,main被唤醒后继续执行。
ps:countdownlatch和cyclicbarrier的区别:
(1) countdownlatch的作用是允许1或n个线程等待其他线程完成执行;而cyclicbarrier则是允许n个线程相互等待。
(2) countdownlatch的计数器无法被重置;cyclicbarrier的计数器可以被重置后使用,因此它被称为是循环的barrier。
推荐阅读
-
详解Java多线程编程中CountDownLatch阻塞线程的方法
-
详解Java多线程编程中互斥锁ReentrantLock类的用法
-
Java多线程程序中synchronized修饰方法的使用实例
-
详解Java多线程编程中线程的启动、中断或终止操作
-
Java多线程编程中synchronized线程同步的教程
-
Java面向对象编程中final关键字的使用方法详解
-
Java多线程编程中synchronized关键字的基础用法讲解
-
详解Java中的线程让步yield()与线程休眠sleep()方法
-
Java面向对象编程中final关键字的使用方法详解
-
Java多线程程序中synchronized修饰方法的使用实例