业务场景一
业务场景形容:假如一条流水线上有三个工作者:worker1,worker2,worker3。有一个工作的实现须要他们三者合作实现,worker3能够开始这个工作的前提是worker1和worker2实现了他们的工作,而worker1和worker2是能够并行他们各自的工作的。
join实现
public class CountDownLatchAndJoin { public static void main(String[] args) throws InterruptedException { // 三个独立的工人线程 worker worker1 = new worker("worker1", (long) (Math.random()*4000)); worker worker2 = new worker("worker2", (long) (Math.random()*4000)); worker worker3 = new worker("worker3", (long) (Math.random()*4000));// worker worker1 = new worker("worker1", 6000);// worker worker2 = new worker("worker2", 5000);// worker worker3 = new worker("worker3", 5000); worker1.start(); worker2.start(); worker1.join(); worker2.join(); System.out.println("筹备工作就绪..."); worker3.start(); } // 工人类 public static class worker extends Thread { // 名字 private String name; //工作工夫 private long time; worker(String name, long time) { this.name = name; this.time = time; } public void run() { try { System.out.println(name + "开始工作"); Thread.sleep(time); System.out.println(name + "工作实现,消耗工夫=" + time); } catch (InterruptedException e) { e.printStackTrace(); } } }}
后果:
worker2开始工作worker1开始工作worker1工作实现,消耗工夫=601worker2工作实现,消耗工夫=2886筹备工作就绪...worker3开始工作worker3工作实现,消耗工夫=686
能够顺利的实现工作,join的工作原理是,不停查看thread是否存活,如果存活则让以后线程永远wait,直到thread线程终止,线程的notifyAll就会被调用,还能够了解为join就是插队的意思
CountDownLatch实现
public class CountDownLatchTest { public static void main(String[] args) throws InterruptedException { // 初始化计数器为2 CountDownLatch countDownLatch = new CountDownLatch(2); // 三个独立的工人线程 worker worker1 = new worker("worker1", (long) (Math.random() * 4000), countDownLatch); worker worker2 = new worker("worker2", (long) (Math.random() * 4000), countDownLatch); worker worker3 = new worker("worker3", (long) (Math.random() * 4000), countDownLatch); worker1.start(); worker2.start(); // 当计数器不为0的时候均期待 countDownLatch.await(); System.out.println("筹备工作就绪..."); worker3.start(); } public static class worker extends Thread { private String name; private long time; private CountDownLatch countDownLatch; worker(String name, long time, CountDownLatch countDownLatch) { this.name = name; this.time = time; this.countDownLatch = countDownLatch; } public void run() { System.out.println(name + " 开始工作了。。。"); // 减一 countDownLatch.countDown(); System.out.println(name + " 工作实现。。。"); } }}
创立一个计数器为2的 CountDownLatch ,让Worker持有这个CountDownLatch 实例,当实现本人的工作后,调用countDownLatch.countDown() 办法将计数器减1。countDownLatch.await() 办法会始终阻塞直到计数器为0,主线程才会持续往下执行。运行后果
worker1 开始工作了。。。worker1 工作实现。。。worker2 开始工作了。。。worker2 工作实现。。。筹备工作就绪...worker3 开始工作了。。。worker3 工作实现。。。
从后果上来看,都解决了问题,然而上面的场景二?
业务场景二
只能CountDownLatch实现
业务场景:假如worker的工作能够分为两个阶段,work3 只须要期待work1和work2实现他们各自工作的第一个阶段之后就能够开始本人的工作了,而不是场景1中的必须期待work1和work2把他们的工作全副实现之后能力开始。这样join就不能够实现了,该当采纳CountDownLatch 来实现。
public class CountDownLatchTest { public static void main(String[] args) throws InterruptedException { // 初始化计数器为5 CountDownLatch countDownLatch = new CountDownLatch(5); // 六个独立的工人线程 worker worker1 = new worker("worker1", (long) (Math.random() * 4000), countDownLatch); worker worker2 = new worker("worker2", (long) (Math.random() * 4000), countDownLatch); worker worker3 = new worker("worker3", (long) (Math.random() * 4000), countDownLatch); worker worker4 = new worker("worker4", (long) (Math.random() * 4000), countDownLatch); worker worker5 = new worker("worker5", (long) (Math.random() * 4000), countDownLatch); worker worker6 = new worker("worker6", (long) (Math.random() * 4000), countDownLatch); worker1.start(); worker2.start(); worker3.start(); worker4.start(); worker5.start(); // 当计数器不为0的时候均期待 countDownLatch.await(); System.out.println("筹备工作就绪..."); worker6.start(); } public static class worker extends Thread { private String name; private long time; private CountDownLatch countDownLatch; worker(String name, long time, CountDownLatch countDownLatch) { this.name = name; this.time = time; this.countDownLatch = countDownLatch; } public void run() { try { System.out.println(name + " 工作开始。。。"); Thread.sleep(time); System.out.println(name + " 第一阶段工作实现。。。用时:" + time); // 计数器减一 countDownLatch.countDown(); // 假如第二阶段的工作都须要两秒实现 Thread.sleep(2000); System.out.println(name + " 第二阶段工作实现。。。用时:" + (time + 2000)); } catch (InterruptedException e) { e.printStackTrace(); } } }}
多运行几次发现:线程6等到后面5个线程的第一阶段全副实现,就开始运行了,运行后果:
worker3 工作开始。。。worker2 工作开始。。。worker1 工作开始。。。worker4 工作开始。。。worker5 工作开始。。。worker3 第一阶段工作实现。。。用时:1410worker5 第一阶段工作实现。。。用时:2022worker2 第一阶段工作实现。。。用时:2273worker1 第一阶段工作实现。。。用时:2856worker3 第二阶段工作实现。。。用时:3410worker4 第一阶段工作实现。。。用时:3430筹备工作就绪...worker6 工作开始。。。worker5 第二阶段工作实现。。。用时:4022worker2 第二阶段工作实现。。。用时:4273worker1 第二阶段工作实现。。。用时:4856worker4 第二阶段工作实现。。。用时:5430worker6 第一阶段工作实现。。。用时:3773worker6 第二阶段工作实现。。。用时:5773
总结:
- 调用thread.join() 办法必须等thread 执行结束,以后线程能力持续往下执行,而CountDownLatch通过计数器提供了更灵便的管制,只有检测到计数器为0以后线程就能够往下执行而不必管相应的thread是否执行结束。
- CountDownLatch底层基于AQS。