业务场景一

业务场景形容:假如一条流水线上有三个工作者: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。