CountDownLatch简介
在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
用给定的计数初始化CountDownLatch,由于调用了countDown()方法,所以在当前计数到达零之前,await()方法会一直受阻塞,之后,会释放所有等待的 线程 ,await的所有后续调用都将立即返回。
和CyclicBarrier的区别?
-
CountDownLatch 的作用是允许1或者N个线程等待其他线程 完成执行;而CyclicBarrier则是允许N个线程相互等待。
-
CountDownLatch的计数器无法重置;CyclicBarrier的计数器可以被重置后使用,因此它被 称为是循环的barrier。
内部用共享锁来实现。
API
CountDownLatch.countDown()
CountDownLatch.await();
代码示例
比如陪媳妇去看病。
医院里边排队的人很多,如果一个人的话,要先看大夫,看完大夫再去排队交钱取药。
现在我们是双核,可以同时做这两个事(多线程)。
假设看大夫花3秒钟,排队交费取药花5秒钟。我们同时搞的话,5秒钟我们就能完成,然后一起回家(回到主线程)。
/**
* 看大夫任务
*/
public class SeeDoctorTask implements Runnable {
private CountDownLatch countDownLatch;
public SeeDoctorTask(CountDownLatch countDownLatch){
this.countDownLatch = countDownLatch;
}
public void run() {
try {
System.out.println("开始看医生");
Thread.sleep(3000);
System.out.println("看医生结束,准备离开病房");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
if (countDownLatch != null)
countDownLatch.countDown();
}
}
}
/**
* 排队的任务
*/
public class QueueTask implements Runnable {
private CountDownLatch countDownLatch;
public QueueTask(CountDownLatch countDownLatch){
this.countDownLatch = countDownLatch;
}
public void run() {
try {
System.out.println("开始在医院药房排队买药....");
Thread.sleep(5000);
System.out.println("排队成功,可以开始缴费买药");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
if (countDownLatch != null)
countDownLatch.countDown();
}
}
}
/**
* 配媳妇去看病,轮到媳妇看大夫时
* 我就开始去排队准备交钱了。
*/
public class CountDownLaunchRunner {
public static void main(String[] args) throws InterruptedException {
long now = System.currentTimeMillis();
CountDownLatch countDownLatch = new CountDownLatch(2);
new Thread(new SeeDoctorTask(countDownLatch)).start();
new Thread(new QueueTask(countDownLatch)).start();
//等待线程池中的2个任务执行完毕,否则一直
countDownLatch.await();
System.out.println("over,回家 cost:"+(System.currentTimeMillis()-now));
}
}
用途
- 确保某个计算在其需要的所有资源都被初始化之后才执行
- 确保某个服务在其依赖的所有其他服务都已经启动之后才启动
- 等待直到每个操作的所有参与者都就绪再执行(比如打麻将时需要等待四个玩家就绪)
案例介绍
主线程中启动多个线程去执行任务,并且主线程需要等待所有子线程执行完毕后去汇总:
- 使用 join 来实现
public class JoinCountDownLatchTest {
public static void main( String[] args ) throws InterruptedException {
Thread t1 = new Thread(()->{
});
Thread t2 = new Thread(()->{
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("all parse finish..");
}
}
- 使用CounDownLatch来实现
public class CountDownLatchTest {
static CountDownLatch c = new CountDownLatch(2);
public static void main( String[] args ) throws InterruptedException {
Thread t1 = new Thread(()->{
c.countDown();
});
Thread t2 = new Thread(()->{
c.countDown();
});
t1.start();
t2.start();
c.await();
System.out.println("all parse finish..");
}
}
CountDownLatch 和 join 的区别
- 调用子线程的join方法后,该线程会一直被阻塞直到子线程运行完毕,而CountDownLatch则使用计数器来允许子线程运行完毕或者在运行中递减计数,也就是CountDownLatch可以在子线程运行的 任何 时候让await方法返回而不一定必须等到线程结束。
- 使用线程池来管理线程时一般都是直接添加Runable线程池,这时候就没有办法再调用线程的join方法了,就是说countDownLatch相比join方法让我们对线程同步有更灵活的控制。