【并发编程】 --- CountDownLatch原理简介 + 使用方法

Eilene ·
更新时间:2024-09-21
· 984 次阅读

文章目录1 原理简介2 具体使用方法2.1 demo1 --- await不传入时间,保证当前线程的其他操作在最后执行2.2 demo2 --- await传入时间t,当前线程等其他线程时间t后就运行其他操作2.3 发令枪

源码地址:https://github.com/nieandsun/concurrent-study.git

其实我自认为CountDownLatch类是进入AQS世界一个非常好非常好的通道,因此写完这篇文章,我打算再写一篇关于CountDownLatch源码的文章。

1 原理简介

CountDownLatch原理可以用下图进行表示:
在这里插入图片描述

这里注意一下: 每个线程都可以调用countDown()1次,或多次 —> 并非每个线程都只能调用1次

其实这个CountDownLatch和Thread的join方法有点类似,下图是我在《【并发编程】— Thread类中的join方法》一文中画的join的原理图,有兴趣的可以对比一下。

在这里插入图片描述

2 具体使用方法 2.1 demo1 — await不传入时间,保证当前线程的其他操作在最后执行 code package com.nrsc.ch2.juctools; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CountDownLatch; @Slf4j public class CountDownLatchDemo1 { private final static int threadCount = 200; public static void main(String[] args) throws InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i { try { test(threadNum); } catch (Exception e) { log.error("exception:", e); } finally { //放在finally块里保证线程无论怎样都会执行countDown方法 countDownLatch.countDown(); } }).start(); } countDownLatch.await(); //可以想到finish肯定会在上面的线程都运行完才执行 log.info("finish"); } private static void test(int i) throws InterruptedException { Thread.sleep(100); log.info("threadNum:{}", i); Thread.sleep(100); } } 测试结果

在这里插入图片描述

2.2 demo2 — await传入时间t,当前线程等其他线程时间t后就运行其他操作 code package com.nrsc.ch2.juctools; import lombok.extern.slf4j.Slf4j; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @Slf4j public class CountDownLatchDemo2 { private final static int threadCount = 200; public static void main(String[] args) throws InterruptedException { final CountDownLatch countDownLatch = new CountDownLatch(threadCount); for (int i = 0; i { try { test(threadNum); } catch (Exception e) { log.error("exception:", e); } finally { countDownLatch.countDown(); } }).start(); } //等其他线程11MILLISECONDS后,放行当前线程await后的其他操作 countDownLatch.await(11, TimeUnit.MILLISECONDS); log.info("finish"); } private static void test(int i) throws InterruptedException { Thread.sleep(10); log.info("threadNum:{}", i); } } 测试结果

在这里插入图片描述
注意: 我上面的运行结果并不是唯一的,其实finish在最后输出也是有可能的。这里我让其他线程睡了10MILLISECONDS,而主线程会等11MILLISECONDS,其他线程是有可能都在11MILLISECONDS之内运行完的。

2.3 发令枪

相信很多人都听说过,CountDownLatch可以做类似发令枪的行为,我这里也实现了一个demo,代码如下:
要准确理解它为什么被比喻成发令枪,一定要多注意一下我代码中的注释,和文末测试结果图中的注释。

package com.nrsc.ch2.juctools; import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; import java.util.List; import java.util.Random; import java.util.concurrent.*; @Slf4j public class CountDownLatchDemo3 { /*** * 继承了Callable的内部类 */ private static class Runner implements Callable { private CountDownLatch judge;//裁判 private CountDownLatch runner;//跑步者 public Runner(CountDownLatch begin, CountDownLatch end) { super(); this.judge = begin; this.runner = end; } @Override public Integer call() throws Exception { int score = new Random().nextInt(10); //------------------------------------------------------------ //judge不进行countDown,所有的线程都会在这里阻塞住 //------------------------------------------------------------ log.info("线程-{}准备就绪", Thread.currentThread().getName()); judge.await(); log.info("线程-{}开始跑步", Thread.currentThread().getName()); //进行跑步 TimeUnit.MILLISECONDS.sleep(score);//跑步需要花的时间 //运动员跑步完成 runner.countDown(); return score; //将执行时间返回 } } public static void main(String[] args) throws InterruptedException { final int runnerNum = 8; CountDownLatch judge = new CountDownLatch(1);//裁判 CountDownLatch runner = new CountDownLatch(runnerNum);//运动员 //新建一个线程池 ExecutorService executorService = Executors.newFixedThreadPool(runnerNum); List<Future> list = new ArrayList(); for (int i = 0; i < runnerNum; i++) { //每个线程都开始运行了,但是都会运行到 judge.await();那里停住,等待judge的countDown方法 Future submit = executorService.submit(new Runner(judge, runner)); list.add(submit); } /*** * 注意,我故意把关闭线程池的操作放在这里了,仍然可以获取到想要的结果 * 这是因为线程池并不会立即关闭,而是将任务执行完才会关闭 */ executorService.shutdown(); //枪声响起 judge.countDown(); //预备,开始,跑!!!! //等待所有的跑步者跑完 runner.await(); log.info("跑步结果:"); //所有跑步者的跑步结果 for (Future future : list) { try { Integer res = future.get(); System.out.print(res + " "); } catch (ExecutionException e) { e.printStackTrace(); } } } } 测试结果

在这里插入图片描述

END


作者:nrsc



并发编程 方法 并发 countdownlatch

需要 登录 后方可回复, 如果你还没有账号请 注册新账号
相关文章