JUC线程进阶篇03:CountDownLatch闭锁
标签: 多线程
CountDownLatch,直译的意思是:倒计时的闩。专业的称呼是:闭锁。
既然是一个“门闩”,那么在闭锁到达结束状态之前,这个门一直都是关闭的,没有任何线程能够通过;当到达结束状态时,会打开门闩,允许所有线程通过。
CountDownLatch是java5中新增的一个并发工具类。可以使一个或多个线程等待一组事件发生,只有其他所有线程的运算全部完成,当前运算才继续执行。
场景
现在我们在子线程中打印50000以内的所有偶数, 然后在主线程中创建5个这样的子线程,并获取他们的执行时间。
错误的写法
如果我像下面这样写,那肯定是不行的。因为6个线程同时并发执行,怎么能计算呢?
public class TestCountDownLatch {
public static void main(String[] args) {
LatchDemo ld = new LatchDemo();
long start = System.currentTimeMillis();
for (int i = 0 ; i < 5 ; i++) {
new Thread(ld).start();
}
long end = System.currentTimeMillis();
System.out.println("所用时间:" + (start-end));
}
}
class LatchDemo implements Runnable{
public void run() {
for (int i = 0 ; i < 50000 ; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
}
}
正确的写法
分析
对于需求,我们希望的是在5个子线程执行完成之后,再获取时间。这就需要一个先后顺序。CountDownLatch直译是倒计时的闩,我们使用CountDownLatch来实现一个计数器,执行一个线程,计数器递减1,直到计数器变为0。在计数器变为0之前,我们用一个“闩”阻塞主程序,直到变为0,主程序才继续执行。
那么对应到代码上,使用CountDownLatch的构造函数接收一个int类型的参数作为计数器:
final CountDownLatch latch = new CountDownLatch(5);
CountDownLatch的方法调用countDown()来递减计数器。这个是一定要执行的,所以最好放在finally中。
latch.countDown();
使用await()方法阻塞主线程,直到计数器为0
latch.await();
代码
import java.util.concurrent.CountDownLatch;
public class TestCountDownLatch {
public static void main(String[] args) {
// 1. 创建一个闭锁,并设置计时器
final CountDownLatch latch = new CountDownLatch(5);
// 2. 将闭锁作为参数,传递到子线程中
LatchDemo ld = new LatchDemo(latch);
long start = System.currentTimeMillis();
for (int i = 0 ; i < 5 ; i++) {
new Thread(ld).start();
}
try {
// 5. 等待计时器归零,才执行下面的语句
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("所用时间:" + (end-start));
}
}
class LatchDemo implements Runnable{
private CountDownLatch latch;
// 3. 子线程需要传入一个闭锁
public LatchDemo (CountDownLatch latch) {
this.latch = latch;
}
public void run() {
synchronized (this) {
try {
for (int i = 0; i < 50000; i++) {
if (i % 2 == 0) {
System.out.println(i);
}
}
} finally {
// 4. 计时器减1
latch.countDown();
}
}
}
}
CountDownLatch与join的区别
调用thread.join() 方法必须等thread执行完毕,当前线程才能继续往下执行,而CountDownLatch通过计数器提供了更灵活的控制,只要检测到计数器为0当前线程就可以往下执行而不用管相应的thread是否执行完毕。