業(yè)務場景一
業(yè)務場景描述:假設一條流水線上有三個工作者:worker1,worker2颠通,worker3。有一個任務的完成需要他們?nèi)邊f(xié)作完成,worker3可以開始這個任務的前提是worker1和worker2完成了他們的工作僻爽,而worker1和worker2是可以并行他們各自的工作的。
join實現(xiàn)
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就會被調(diào)用,還可以理解為join就是插隊的意思
CountDownLatch實現(xiàn)
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
// 初始化計數(shù)器為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();
// 當計數(shù)器不為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 + " 工作完成滓鸠。雁乡。。");
}
}
}
創(chuàng)建一個計數(shù)器為2的 CountDownLatch 糜俗,讓Worker持有這個CountDownLatch 實例踱稍,當完成自己的工作后,調(diào)用countDownLatch.countDown() 方法將計數(shù)器減1悠抹。countDownLatch.await() 方法會一直阻塞直到計數(shù)器為0珠月,主線程才會繼續(xù)往下執(zhí)行。
運行結果
worker1 開始工作了楔敌。啤挎。。
worker1 工作完成卵凑。庆聘。。
worker2 開始工作了勺卢。伙判。。
worker2 工作完成黑忱。宴抚。。
準備工作就緒...
worker3 開始工作了甫煞。菇曲。。
worker3 工作完成抚吠。羊娃。。
從結果上來看埃跷,都解決了問題蕊玷,但是下面的場景二邮利?
業(yè)務場景二
只能CountDownLatch實現(xiàn)
業(yè)務場景:假設worker的工作可以分為兩個階段,work3 只需要等待work1和work2完成他們各自工作的第一個階段之后就可以開始自己的工作了垃帅,而不是場景1中的必須等待work1和work2把他們的工作全部完成之后才能開始延届。這樣join就不可以實現(xiàn)了,應當采用CountDownLatch 來實現(xiàn)贸诚。
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
// 初始化計數(shù)器為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();
// 當計數(shù)器不為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);
// 計數(shù)器減一
countDownLatch.countDown();
// 假設第二階段的工作都需要兩秒完成
Thread.sleep(2000);
System.out.println(name + " 第二階段工作完成龄减。。班眯。用時:" + (time + 2000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
多運行幾次發(fā)現(xiàn):線程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
總結:
- 調(diào)用thread.join() 方法必須等thread 執(zhí)行完畢省艳,當前線程才能繼續(xù)往下執(zhí)行娘纷,而CountDownLatch通過計數(shù)器提供了更靈活的控制,只要檢測到計數(shù)器為0當前線程就可以往下執(zhí)行而不用管相應的thread是否執(zhí)行完畢跋炕。
- CountDownLatch底層基于AQS赖晶。