CountDownLatch的和join有类似之处,但是比join强大并且灵活许多。
- 在实例化一个CountDownLatch时,需要设置一个初始计数,所有调用await()方法的线程都会阻塞直到计数减为零,countDown()方法每调用一次 ,计数减一直到减为零从而唤醒其他所有调用await()方法的线程。
- CountDownLatch的计数功能是一次性的,即它的计数减为零时,不能再重新设置它的值。
- 当CountDownLatch初始化计数设置为1时,它可以作为一个开关或者门(a simple on/off latch, or gate),即所有调用await()方法的线程都必须等待一个线程调用CountDown()方法,那么就相当于一个开关,只有调用countDown()之后线程才可以通过。
- 当CountDownLatch初始计数置为N时,调用await()方法的线程必须等待N个线程调用完成某个事件。
- 和join最大的区别是等待线程是等待其他N个线程调用CountDown()方法而不是线程结束。
来看一个例子(一个货车司机和几个码头工人,首先码头工人需要等待火车司机把车开到码头才能装货,其次货车司机需要等待所有的工人将货物装上车它才能把车开走)
public class Diver {
public static void main(String[] args) throws InterruptedException{
CountDownLatch canDrive = new CountDownLatch(5);//有五个工人
CountDownLatch canLoad = new CountDownLatch(1);
//五个工人在等待
for(int i = 0; i < 5; i++){
new Thread(new Worker(canDrive,canLoad),"" + i).start();
}
Thread.sleep(400);//让工人线程先执行
System.out.println("老司机把车开来了");
canLoad.countDown();
canDrive.await();
System.out.println("老司机把车开走了");
}
}
class Worker implements Runnable{
private CountDownLatch canDrive;//开车开关
private CountDownLatch canLoad;//装货开关
public Worker (CountDownLatch canDrive, CountDownLatch canLoad){
this.canDrive = canDrive;
this.canLoad = canLoad;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + ": 在等车来啊");
canLoad.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ": 我把货装好了");
canDrive.countDown();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "装完货我得抽根烟冷静一下");
}
}
result:
0: 在等车来啊
1: 在等车来啊
2: 在等车来啊
3: 在等车来啊
4: 在等车来啊
老司机把车开来了
0: 我把货装好了
1: 我把货装好了
3: 我把货装好了
4: 我把货装好了
2: 我把货装好了
老司机把车开走了
0装完货我得抽根烟冷静一下
1装完货我得抽根烟冷静一下
3装完货我得抽根烟冷静一下
2装完货我得抽根烟冷静一下
4装完货我得抽根烟冷静一下