文章目录
I CountDownLatch 使用场景举例
II CountDownLatch 简单线程阻塞示例
III CountDownLatch 多个线程联合阻塞示例
I CountDownLatch 使用场景举例
1. 单个阻塞等待单个线程 : 初始化 CountDownLatch 时 , 设置其计数为 1 , 在线程 A 中调用 await() 阻塞 , 然后在线程 B 中执行操作 , 之后调用 countDown() 方法 , 计数 - 1 , 线程 A 阻塞解除 ;
2. 单个阻塞等待多个线程 : 初始化 CountDownLatch 时 , 设置其计数为 2 , 在线程 A 中调用 await() 阻塞 ; 然后在线程 B 中执行操作 , 调用 countDown() 方法 , 计数 - 1 ; 同时在线程 C 中执行更长时间的操作 , 调用 countDown() 方法 , 计数 - 1 ; 线程 B 和 C 的操作执行完毕后 , 其计数才减为 0 , 此时线程 A 中的阻塞解除 ;
3. 多个线程阻塞等待单个线程 : 多个线程中调用 CountDownLatch 对象 await() 方法阻塞 , 在另外一个线程中将计数 countDown() 为 0 , 这些线程即可执行 ;
4. 多个线程阻塞等待多个线程 : 多个线程中调用 CountDownLatch 对象 await() 方法阻塞 , 在另外多个线程中将计数 countDown() 为 0 , 被阻塞这些线程即可执行 ;
5. CountDownLatch 使用场景 :
① 单线程等待单线程 : 线程 A 阻塞 , 等待线程 B 执行完毕后 , 在执行线程 A 操作 ;
② 单线程等待多线程 : 线程 A 阻塞 , 等待线程 B , C , D 等线程执行完毕 , 在执行线程 A 操作 ;
③ 单线程与多线程互相阻塞 : 线程 B , C , D , 先被 new CountDownLatch ( 1 ) 对象阻塞住 , 在线程 A 中先解除 B , C , D 的阻塞 , 然后 B , C , D 这三个线程才能继续执行 , 线程 A 解除之后 , 马上被 new CountDownLatch ( 3 ) 对象阻塞 , B , C , D 三个线程执行完后 , 每个线程计数减一 , 之后解除线程 A 阻塞 , 继续执行线程 A 的内容 ;
④ 单线程与多线程互相阻塞并设置超时时间 : 在上述 ③ 情况的基础上 , 加上超时等待 , 如果 B , C , D 线程在指定时间内没有执行完毕 , 那么线程 A 也解除阻塞 , 继续向下执行之后的代码 ;
II CountDownLatch 简单线程阻塞示例
1. 代码说明 : 子线程运行后调用 CountDownLatch 的 await 方法阻塞 , 在主线程中调用 countDown 方法将计数减为 0 , 子线程解除阻塞 ;
2. 代码示例 :
import java.util.concurrent.CountDownLatch; /** * 子线程运行后调用 CountDownLatch 的 await 方法阻塞 , * 在主线程中调用 countDown 方法将计数减为 0 , 子线程解除阻塞 */ public class CountDownLatchDemo { public static void main(String[] args) { System.out.println("1. 主线程 : 开始运行 , 创建 CountDownLatch 对象初始计数为 1"); //创建 CountDownLatch 对象 , 初始计数为 1 CountDownLatch countDownLatch = new CountDownLatch(1); System.out.println("2. 主线程 : 创建子线程并运行"); //创建子线程 , 并设置其 countDownLatch 对象, 运行子线程 MyThread myThread = new MyThread(countDownLatch); myThread.start(); System.out.println("3. 主线程 : 调用 countDownLatch.countDown() 方法"); countDownLatch.countDown(); System.out.println("4. 主线程 : 运行结束"); } static class MyThread extends Thread{ /** * 用于阻塞的 CountDownLatch 对象 */ CountDownLatch countDownLatch; /** * 主线程中传入 CountDownLatch 对象 , 两个线程公用一个该对象 * @param countDownLatch */ public MyThread(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { super.run(); try { System.out.println("1. 子线程 : 开始运行 , 并调用 countDownLatch.await() 方法阻塞"); //阻塞子线程 countDownLatch.await(); System.out.println("2. 子线程 : CountDownLatch 对象计数为 0 , 子线程继续运行并结束"); } catch (InterruptedException e) { e.printStackTrace(); } } } }
3. 执行结果 :
1. 主线程 : 开始运行 , 创建 CountDownLatch 对象初始计数为 1 2. 主线程 : 创建子线程并运行 3. 主线程 : 调用 countDownLatch.countDown() 方法 1. 子线程 : 开始运行 , 并调用 countDownLatch.await() 方法阻塞 4. 主线程 : 运行结束 2. 子线程 : CountDownLatch 对象计数为 0 , 子线程继续运行并结束
III CountDownLatch 多个线程联合阻塞示例
1. 情景描述 : 运动员赛跑 , 1 个裁判 , 4 个运动员 , 4 个运动员首先等待裁判发令 , 才能开始跑 , 裁判发令后在终点等待 4 个运动员都达到终点后 , 在宣布成绩 ;
2. 线程模型分析 :
① 线程 : 裁判员是一个单独的线程 , 4 个运动员是 4 个独立的线程 ;
② CountDownLatch : 两种 CountDownLatch 对象 , 一个用于阻塞裁判员线程 , 一个用于阻塞运动员线程 ;
③ 运动员线程 : 四个运动员线程一开始运行后 , 马上调用 new CountDownLatch(1) 对象阻塞住 , 不能向后运行 ;
④ 裁判员线程 : 裁判员线程要等四个运动员线程启动后才能执行 , 先调用 countDown 将四个运动员线程取消阻塞 , 然后调用new CountDownLatch(4) 对象 的 await 阻塞 , 每个运动员线程跑到终点后 , 调用 countDown 方法 , 四个运动员全部到达终点后 , 裁判员解除阻塞 , 宣布成绩 ;
3. 代码示例 :
i
mport java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * 情景描述 : 运动员赛跑 , 1 个裁判 , 4 个运动员 , * 4 个运动员首先等待裁判发令 , 才能开始跑 , * 裁判发令后在终点等待 4 个运动员都达到终点后 , 在宣布成绩 ; * * 线程模型分析 : * * ① 线程 : 裁判员是一个单独的线程 , 4 个运动员是 4 个独立的线程 ; * ② CountDownLatch : 两种 CountDownLatch 对象 , 一个用于阻塞裁判员线程 , 一个用于阻塞运动员线程 ; * ③ 运动员线程 ( 子线程 ) : 四个运动员线程一开始运行后 , 马上调用 new CountDownLatch(1) 对象阻塞住 , 不能向后运行 ; * ④ 裁判员线程 ( 主线程 ) : 裁判员线程要等四个运动员线程启动后才能执行 , 先调用 countDown 将四个运动员线程取消阻塞 , * 然后调用new CountDownLatch(4) 对象 的 await 阻塞 , 每个运动员线程跑到终点后 , * 调用 countDown 方法 , 四个运动员全部到达终点后 , 裁判员解除阻塞 , 宣布成绩 ; */ public class CountDownLatchDemo { public static void main(String[] args) throws InterruptedException { //用于存储四个运动员的成绩 int[] grades = new int[4]; //四个运动员线程的线程池 ExecutorService executorService = Executors.newCachedThreadPool(); //阻塞运动员线程的倒计时锁对象 , 需要裁判员线程解锁 CountDownLatch runnerLatch = new CountDownLatch(1); //阻塞裁判线程的倒计时锁对象 , 需要四个运动员线程解锁 CountDownLatch judgeLatch = new CountDownLatch(4); //创建并执行运动员线程 , 使用线程池机制执行 for(int i = 0; i < 4; i ++){ int finalI = i; //创建运动员线程 Runnable runnable = new Runnable() { @Override public void run() { try { System.out.println( finalI + " 号运动员准备完毕 , 等待裁判员发令"); runnerLatch.await(); System.out.println( finalI + " 号运动员起跑"); //设置运动员成绩 , 这里用一个随机数代替 grades[finalI] = (int) (Math.random() * 10000); Thread.sleep(grades[finalI]); //通知裁判员到达终点 judgeLatch.countDown(); System.out.println( finalI + " 号运动员到达终点"); } catch (InterruptedException e) { e.printStackTrace(); } } }; //使用线程池调度运行该线程 executorService.execute(runnable); } System.out.println("裁判员 : 发令 , 起跑"); //裁判员线程在运动员准备完毕后 , 解除上述 4 个运动员线程的阻塞 , 即运动员起跑 runnerLatch.countDown(); System.out.println("裁判员 : 在终点等待 4 名运动员"); //裁判员线程阻塞, 等待 4 个运动员线程执行完毕 judgeLatch.await(); System.out.println("裁判员 : 运动员全部到达终点成绩为 0 号 : " + grades[0] + " , 1 号 : " + grades[1] + " , 2 号 : " + grades[2] + " , 3 号 : " + grades[3]); } }
4. 执行结果 :
裁判员 : 发令 , 起跑 裁判员 : 在终点等待 4 名运动员 2 号运动员准备完毕 , 等待裁判员发令 2 号运动员起跑 1 号运动员准备完毕 , 等待裁判员发令 1 号运动员起跑 0 号运动员准备完毕 , 等待裁判员发令 0 号运动员起跑 3 号运动员准备完毕 , 等待裁判员发令 3 号运动员起跑 1 号运动员到达终点 2 号运动员到达终点 0 号运动员到达终点 3 号运动员到达终点 裁判员 : 运动员全部到达终点成绩为 0 号 : 5601 , 1 号 : 1763 , 2 号 : 4700 , 3 号 : 9650