Java并发之CountDownLatch
定义
CountDownLatch
从意思上看,countdown
是倒数的意思,latch
的意思是门栓,所以看作是倒数门栓。CountDownLatch
的作用也和他的意思一样,主线程可以看作是一个门栓,等到其他线程倒数完成之后,主线程才能继续向下执行。简单来说就是允许一个或者多个线程等待其他线程完成操作。
方法
构造方法,传入需要计数的次数,在
count>0
之前,线程会一直等待
public CountDownLatch(int count)
调用此方法的线程在计数器为0之前会一直等待,可以由多个线程同时调用此方法,他们都将会等待
public void await() throws InterruptedException
计数器减1,一般是执行任务的方法进行调用
public void countDown()
应用场景
为了对比,这里使用相同的场景,和Java并发之CyclicBarrier的例子一样,但是我们先不使用CountDownLatch
来实现这个功能,代码如下:
public class Test {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(() -> {
System.out.println("1号选手 开始准备");
try {
Thread.sleep(2000);
System.out.println("1号选手 准备完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
Thread t2 = new Thread(() -> {
System.out.println("2号选手 开始准备");
try {
Thread.sleep(2000);
System.out.println("2号选手 准备完成");
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println("选手们准备完成");
}
}
输出如下
1号选手 开始准备
2号选手 开始准备
1号选手 准备完成
2号选手 准备完成
选手们准备完成
这里使用了Thread类的join()方法来让主线程进行等待,等待t1和t2执行完成后才能继续执行。join()方法会一直检查线程是否存活,如果存活则会一直等待下去。
用CountDownLatch
实现更加灵活,比join()的功能更多,代码如下:
public class MyThread implements Runnable {
private String name;
private CountDownLatch countDownLatch;
public MyThread(String name, CountDownLatch countDownLatch) {
this.name = name;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try {
System.out.println(this.name + " 开始准备");
Thread.sleep(1000 * 3);
System.out.println(this.name + " 准备完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
}
}
public static void main(String[] args) {
int corePoolSize = Runtime.getRuntime().availableProcessors() * 2;
CountDownLatch countDownLatch = new CountDownLatch(corePoolSize);
ExecutorService executorService = new ThreadPoolExecutor(corePoolSize, corePoolSize, 0L,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
for (int i = 1; i <= corePoolSize; i++) {
executorService.execute(new MyThread(i + "号选手", countDownLatch));
}
try {
countDownLatch.await();
System.out.println("选手们准备完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
executorService.shutdown();
}
}
可以看到在主线程中使用了await()
方法,在其他线程执行countdown()
方法,在计数器为0之前,主线程会一直等待。在真实开发中,如果某个线程执行时间过长,也可以使用带时间参数的await(long time, TimeUnit unit)
,这个方法等待指定时间后,就不会再等待了,join()
方法也有类似的时间参数
总结
和CyclicBarrier
不同的是,CountDownLatch
不能进行重置,只能使用一次。CyclicBarrier
功能也更为丰富,可以参看CyclicBarrier
的API。CountDownLatch一般用于某个线程等待若干个其他线程执行完任务之后,它才执行,而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行。
参考
- 《Java并发编程的艺术》