假设个场景来聊聊CountDownLatch的使用

52 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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();

我们执行下代码,看看能否正确的模拟出这个场景。

image.png image.png

实际上每次执行的效果都不一样,老师也并没有等待所有学生都来到教室再上课,所以上面的代码还需要一定的修改。

问题点

五个同学可能同时进入教室的代码没啥问题,关键是等待五个同学全部进入教室,老师再上课的场景没有很好的实现。

这时候就需要引用一个同步辅助类: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("小汪老师开始上课");

这时候执行代码就会发现问题解决了,老师每次都会等待所有学生都进入教室后再上课。

image.png

但实际上,老师不可能无限等待学生都进入教室,所以得需要设置一个等待时间,并且需要一个等待的结果去进行不同的操作。这就需要引入

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("还有学生未进入教室,无法上课,需要进行寻找!");
}

执行代码,看看是否达到效果。

image.png

综上,可以看到我们使用CountDownLatch很好的实现了老师等待所有学生进入教室才能上课的场景,并且设置了等待时间,根据等待的结果进行相应的操作。

希望能帮助大家更好的理解CountDownLatch的使用。