一.CountDownLatch是干嘛的?
CountDownLatch 计数器,所属java.util.concurrent。内部有一个count值,同时只有一个线程可以操作count,如果调用了await()方法,线程会进入阻塞状态,直到count为0才会继续执行。
二.CountDownLatch内部结构:
1)其有继承AbstractQueuedSynchronizer的Sync 内部类维护count变量(0表示当前锁空闲,1为锁被占用),可通过构造器初始化count,如图:
2)包含方法:
1.构造器CountDownLatch(int count):初始化count,必须大于0
2.await() :导致当前线程进入等待直到计数器的调用countDown()并将count变为0
3.await(long,TimeUnit):进入阻塞状态,直到count为0或者被中断或者超时了
4.countDown():会将count值减一,如果count为0则释放所有的等待线程
5.getCount():获取count值
三。使用:
1)。场景一:主线程要等两个子线程执行完之后才执行某种逻辑(excel有两个文本,为了提高效率用两个线程分别执行,但是要统计两个文本的总行数)。两个线程执行完都会执行countDown(),await后直到两个线程都执行完之后才执行方法体:
/**
* 测试CountDownLatch
*/
@Test
public void testCountDownLatch() {
final CountDownLatch countDownLatch = new CountDownLatch(2) {
@Override
public void await() throws InterruptedException {
System.out.println("调用了await");
//进入阻塞状态,直到countDownLatch的count变为0
super.await();
System.out.println(Thread.currentThread().getName() + "count 变为0了,阻塞结束");
}
};
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName() + "线程一count减一");
} catch (InterruptedException e) {
e.printStackTrace();
}
countDownLatch.countDown();
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(20000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "线程二count减一");
countDownLatch.countDown();
}
});
try {
thread1.start();
thread2.start();
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
执行结果:
2)场景二:运动场,有5个运动员进行比赛,模拟统一喊开始。启动一个开始计数器(begin)和标记是否结束的计数器(end):
/**
* 5个人同时进行赛跑,并得到所有人的跑完顺序
*/
@Test
public void testCountDownLatchRunMatch(){
//统一喊开始
final CountDownLatch begin = new CountDownLatch(1);
//总人数,!!如果算错人数将会进入永久等待
int totalPerson = 5;
final CountDownLatch end = new CountDownLatch(totalPerson);
for(int i = 0;i<totalPerson;i++){
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName()+"准备就绪");
//进入阻塞,知道主线程统一喊开始
begin.await();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"跑完了");
//告诉计数器,我跑完了
end.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
}
//主线程喊开始
begin.countDown();
try {
System.out.println("before await end count is "+end.getCount());
//知道所有人跑完了才继续执行
end.await();
System.out.println("after await end count is "+end.getCount());
System.out.println("所有人都跑完了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
运行结果:
3)其他场景,诸如此类:要煮茶,没开水、没茶叶、洗茶具,先插电煮5分钟开水,然后网上下单让快递员送茶叶上门,然后洗茶具,三件事都做完了就可以泡茶了。
四。总结:
1)需要等待多个资源就绪统一执行
2)切结计算好count,算错了会死的很惨