业务场景一
业务场景形容:假如一条流水线上有三个工作者: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 工作实现,消耗工夫 =601
worker2 工作实现,消耗工夫 =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 第一阶段工作实现。。。用时:1410
worker5 第一阶段工作实现。。。用时:2022
worker2 第一阶段工作实现。。。用时:2273
worker1 第一阶段工作实现。。。用时:2856
worker3 第二阶段工作实现。。。用时:3410
worker4 第一阶段工作实现。。。用时:3430
筹备工作就绪...
worker6 工作开始。。。worker5 第二阶段工作实现。。。用时:4022
worker2 第二阶段工作实现。。。用时:4273
worker1 第二阶段工作实现。。。用时:4856
worker4 第二阶段工作实现。。。用时:5430
worker6 第一阶段工作实现。。。用时:3773
worker6 第二阶段工作实现。。。用时:5773
总结:
- 调用 thread.join() 办法必须等 thread 执行结束,以后线程能力持续往下执行,而 CountDownLatch 通过计数器提供了更灵便的管制,只有检测到计数器为 0 以后线程就能够往下执行而不必管相应的 thread 是否执行结束。
- CountDownLatch 底层基于 AQS。