关于java:CountDownLatch

69次阅读

共计 4416 个字符,预计需要花费 12 分钟才能阅读完成。

业务场景一

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

正文完
 0