开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情
假设场景
假设我们有这样一个场景:一个班级有五个学生,老师需要等待五个学生都进入班级才能开始上课。
这时,我们用代码该如何实现这个场景呢?
代码实现
初步实现
先初始化五个同学:
List<String> students = Arrays.asList("小明同学","小潘同学","小刘同学","小李同学","小胡同学");
再来模拟五个同学可能同时进入教室(假设从不同的门进入),然后老师上课。
这里就用线程池来模拟并发进入了。
ExecutorService service = ThreadPoolUtil.newCommonThreadPool();
service.execute(() -> {
System.out.println(students.get(0) + "走进教室并坐好");
});
service.execute(() -> {
System.out.println(students.get(1) + "走进教室并坐好");
});
service.execute(() -> {
System.out.println(students.get(2) + "走进教室并坐好");
});
service.execute(() -> {
System.out.println(students.get(3) + "走进教室并坐好");
});
service.execute(() -> {
System.out.println(students.get(4) + "走进教室并坐好");
});
System.out.println("小汪老师开始上课");
service.shutdown();
我们执行下代码,看看能否正确的模拟出这个场景。
实际上每次执行的效果都不一样,老师也并没有等待所有学生都来到教室再上课,所以上面的代码还需要一定的修改。
问题点
五个同学可能同时进入教室的代码没啥问题,关键是等待五个同学全部进入教室,老师再上课的场景没有很好的实现。
这时候就需要引用一个同步辅助类:CountDownLatch了。
代码完善
我们首先为CountDownLatch设置一个学生的总数
CountDownLatch countDownLatch = new CountDownLatch(students.size());
然后每次学生进入教室后countdown,也就是数量-1;在最后.await()等待所有学生都进入教室,老师再上课。
service.execute(() -> {
System.out.println(students.get(0) + "走进教室并坐好");
countDownLatch.countDown();
});
service.execute(() -> {
System.out.println(students.get(1) + "走进教室并坐好");
countDownLatch.countDown();
});
service.execute(() -> {
System.out.println(students.get(2) + "走进教室并坐好");
countDownLatch.countDown();
});
service.execute(() -> {
System.out.println(students.get(3) + "走进教室并坐好");
countDownLatch.countDown();
});
service.execute(() -> {
System.out.println(students.get(4) + "走进教室并坐好");
countDownLatch.countDown();
});
countDownLatch.await();
System.out.println("小汪老师开始上课");
这时候执行代码就会发现问题解决了,老师每次都会等待所有学生都进入教室后再上课。
但实际上,老师不可能无限等待学生都进入教室,所以得需要设置一个等待时间,并且需要一个等待的结果去进行不同的操作。这就需要引入
public boolean await(long timeout, TimeUnit unit)
假如有个学生进入教室之前耽误了3秒,而老师只等待所有学生2秒的时间进入教室。如果超过2秒,还有学生没有进入教室就无法上课,需要寻找为进入教室的学生。
service.execute(() -> {
System.out.println(students.get(0) + "走进教室并坐好");
countDownLatch.countDown();
});
service.execute(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(students.get(1) + "走进教室并坐好");
countDownLatch.countDown();
});
service.execute(() -> {
System.out.println(students.get(2) + "走进教室并坐好");
countDownLatch.countDown();
});
service.execute(() -> {
System.out.println(students.get(3) + "走进教室并坐好");
countDownLatch.countDown();
});
service.execute(() -> {
System.out.println(students.get(4) + "走进教室并坐好");
countDownLatch.countDown();
});
// 只有当计数减为0的时候 返回值才会是true 等待不足2秒或者超过2秒都不会影响返回值
boolean await = countDownLatch.await(2, TimeUnit.SECONDS);
service.shutdown();
if (await){
System.out.println("小汪老师开始上课");
}else {
throw new RuntimeException("还有学生未进入教室,无法上课,需要进行寻找!");
}
执行代码,看看是否达到效果。
综上,可以看到我们使用CountDownLatch很好的实现了老师等待所有学生进入教室才能上课的场景,并且设置了等待时间,根据等待的结果进行相应的操作。
希望能帮助大家更好的理解CountDownLatch的使用。