今天学习 CountDownLatch 这个类, 作用感觉和 join 很像, 然后就百度了一下, 看了他们之间的区别. 所以在此记录一下.
首先来看一下 join, 在当前线程中, 如果调用某个 thread 的 join 方法, 那么当前线程就会被阻塞, 直到 thread 线程执行完毕, 当前线程才能继续执行. join 的原理是, 不断的检查 thread 是否存活, 如果存活, 那么让当前线程一直 wait, 直到 thread 线程终止, 线程的 this.notifyAll 就会被调用.
我们来看一下这个应用场景: 假设现在公司有三个员工 A,B,C, 他们要开会. 但是 A 需要等 B,C 准备好之后再才能开始, B,C 需要同时准备. 我们先用 join 模拟上面的场景.
- Employee.java:
- public class Employee extends Thread{
- private String employeeName;
- private long time;
- public Employee(String employeeName,long time){
- this.employeeName = employeeName;
- this.time = time;
- }
- @Override
- public void run() {
- try {
- System.out.println(employeeName+ "开始准备");
- Thread.sleep(time);
- System.out.println(employeeName+"准备完成");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- JoinTest.java:
- public class JoinTest {
- public static void main(String[] args) throws InterruptedException {
- Employee a = new Employee("A", 3000);
- Employee b = new Employee("B", 3000);
- Employee c = new Employee("C", 4000);
- b.start();
- c.start();
- b.join();
- c.join();
- System.out.println("B,C 准备完成");
- a.start();
- }
- }
最后输出结果如下:
C 开始准备
B 开始准备
B 准备完成
C 准备完成
B,C 准备完成
A 开始准备
A 准备完成
可以看到, A 总是在 B,C 准备完成之后才开始执行的.
CountDownLatch 中我们主要用到两个方法一个是 await() 方法, 调用这个方法的线程会被阻塞, 另外一个是 countDown() 方法, 调用这个方法会使计数器减一, 当计数器的值为 0 时, 因调用 await() 方法被阻塞的线程会被唤醒, 继续执行.
接下来, 我们用 CountDownLatch 来模拟一下.
- Employee.java:
- public class Employee extends Thread{
- private String employeeName;
- private long time;
- private CountDownLatch countDownLatch;
- public Employee(String employeeName,long time, CountDownLatch countDownLatch){
- this.employeeName = employeeName;
- this.time = time;
- this.countDownLatch = countDownLatch;
- }
- @Override
- public void run() {
- try {
- System.out.println(employeeName+ "开始准备");
- Thread.sleep(time);
- System.out.println(employeeName+"准备完成");
- countDownLatch.countDown();
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
- CountDownLatchTest.java:
- public class CountDownLatchTest {
- public static void main(String[] args) throws InterruptedException {
- CountDownLatch countDownLatch = new CountDownLatch(2);
- Employee a = new Employee("A", 3000,countDownLatch);
- Employee b = new Employee("B", 3000,countDownLatch);
- Employee c = new Employee("C", 4000,countDownLatch);
- b.start();
- c.start();
- countDownLatch.await();
- System.out.println("B,C 准备完成");
- a.start();
- }
- }
输出结果如下:
B 开始准备
C 开始准备
B 准备完成
C 准备完成
B,C 准备完成
A 开始准备
A 准备完成
上面可以看到, CountDownLatch 与 join 都能够模拟上述的场景, 那么他们有什么不同呢? 这时候我们试想另外一个场景就能看到他们的区别了.
假设 A,B,C 的工作都分为两个阶段, A 只需要等待 B,C 各自完成他们工作的第一个阶段就可以执行了.
我们来修改一下 Employee 类:
- public class Employee extends Thread{
- private String employeeName;
- private long time;
- private CountDownLatch countDownLatch;
- public Employee(String employeeName,long time, CountDownLatch countDownLatch){
- this.employeeName = employeeName;
- this.time = time;
- this.countDownLatch = countDownLatch;
- }
- @Override
- public void run() {
- try {
- System.out.println(employeeName+ "第一阶段开始准备");
- Thread.sleep(time);
- System.out.println(employeeName+"第一阶段准备完成");
- countDownLatch.countDown();
- System.out.println(employeeName+ "第二阶段开始准备");
- Thread.sleep(time);
- System.out.println(employeeName+"第二阶段准备完成");
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }
CountDownLatchTest 类不需要做修改, 输出结果入下:
B 第一阶段开始准备
C 第一阶段开始准备
B 第一阶段准备完成
B 第二阶段开始准备
C 第一阶段准备完成
C 第二阶段开始准备
B,C 第一阶段准备完成
A 第一阶段开始准备
B 第二阶段准备完成
A 第一阶段准备完成
A 第二阶段开始准备
C 第二阶段准备完成
A 第二阶段准备完成
从结果可以看出, A 在 B,C 第一阶段准备完成的时候就开始执行了, 不需要等到第二阶段准备完成. 这种场景下, 用 join 是没法实现的.
总结: 调用 join 方法需要等待 thread 执行完毕才能继续向下执行, 而 CountDownLatch 只需要检查计数器的值为零就可以继续向下执行, 相比之下, CountDownLatch 更加灵活一些, 可以实现一些更加复杂的业务场景.
参考: https://blog.csdn.net/zhutulang/article/details/48504487
来源: https://www.cnblogs.com/junzhao/p/8822260.html