Java并发编程核心方法与框架-CountDownLatch的使用
2016-07-14 20:23
806 查看
Java多线程编程中经常会碰到这样一种场景:某个线程需要等待一个或多个线程操作结束(或达到某种状态)才开始执行。比如裁判员需要等待运动员准备好后才发送开始指令,运动员要等裁判员发送开始指令后才开始比赛。
程序运行结果如下:
五个线程在main线程执行begin.countDown()后同时开始执行,每个线程执行完毕后都会执行end.countDown(),main线程等待end状态为0时停止执行。
再看一个例子:
程序运行结果如下:
程序执行原理和前一个例子相同。
CountDownLatch工作原理相对简单,可以简单看成一个倒计时器,在构造方法中指定初始值,每次调用countDown()方法时讲计数器减1,而await()会等待计数器变为0。CountDownLatch关键接口如下
countDown() 如果当前计数器的值大于1,则将其减1;若当前值为1,则将其置为0并唤醒所有通过await等待的线程;若当前值为0,则什么也不做直接返回。
await() 等待计数器的值为0,若计数器的值为0则该方法返回;若等待期间该线程被中断,则抛出InterruptedException并清除该线程的中断状态。
await(long timeout, TimeUnit unit) 在指定的时间内等待计数器的值为0,若在指定时间内计数器的值变为0,则该方法返回true;若指定时间内计数器的值仍未变为0,则返回false;若指定时间内计数器的值变为0之前当前线程被中断,则抛出InterruptedException并清除该线程的中断状态。
getCount() 读取当前计数器的值,一般用于调试或者测试。
一个完整的比赛流程:
程序运行结果如下:
public class Player implements Runnable { private int id; private CountDownLatch begin; private CountDownLatch end; public Player(int i, CountDownLatch begin, CountDownLatch end) { super(); this.id = i; this.begin = begin; this.end = end; } @Override public void run() { try { begin.await();// 等待begin的状态为0时开始 System.out.println("Play" + id + "开始时间:" + System.currentTimeMillis()); Thread.sleep((long) (Math.random() * 100));// 随机分配时间,即运动员完成时间 System.out.println("Play" + id + " arrived."); } catch (InterruptedException e) { e.printStackTrace(); } finally { end.countDown();// 使end状态减1,最终减至0 } } } public class CountDownLatchDemo { private static final int PLAYER_AMOUNT = 5; public static void main(String[] args) { //对于每位运动员,CountDownLatch减1后即结束比赛 CountDownLatch begin = new CountDownLatch(1);// 相当于裁判员 //对于整个比赛,所有运动员结束后才算结束 CountDownLatch end = new CountDownLatch(PLAYER_AMOUNT); Player[] plays = new Player[PLAYER_AMOUNT]; for (int i = 0; i < PLAYER_AMOUNT; i++) { plays[i] = new Player(i + 1, begin, end); } // 设置特定的线程池,大小为5 ExecutorService exe = Executors.newFixedThreadPool(PLAYER_AMOUNT); for (Player p : plays) { exe.execute(p);// 分配线程 } System.out.println("Race begins!"); begin.countDown(); try { end.await();// 等待end状态变为0,即为比赛结束 } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println("Race ends!"); } exe.shutdown(); } }
程序运行结果如下:
Race begins! Play1开始时间:1469438682994 Play4开始时间:1469438682994 Play2开始时间:1469438682994 Play3开始时间:1469438682994 Play5开始时间:1469438682994 Play3 arrived. Play4 arrived. Play1 arrived. Play5 arrived. Play2 arrived. Race ends!
五个线程在main线程执行begin.countDown()后同时开始执行,每个线程执行完毕后都会执行end.countDown(),main线程等待end状态为0时停止执行。
再看一个例子:
public class CountDownLatchTest { // 模拟了100米赛跑,10名选手已经准备就绪,只等裁判一声令下。当所有人都到达终点时,比赛结束。 public static void main(String[] args) throws InterruptedException { // 开始的倒数锁 final CountDownLatch begin = new CountDownLatch(1); // 结束的倒数锁 final CountDownLatch end = new CountDownLatch(10); // 十名选手 final ExecutorService exec = Executors.newFixedThreadPool(10); for (int index = 0; index < 10; index++) { final int NO = index + 1; Runnable run = new Runnable() { public void run() { try { // 如果当前计数为零,则此方法立即返回。 // 等待 begin.await(); Thread.sleep((long) (Math.random() * 10000)); System.out.println("No." + NO + " arrived"); } catch (InterruptedException e) { } finally { // 每个选手到达终点时,end就减一 end.countDown(); } } }; exec.submit(run); } System.out.println("Game Start"); // begin减一,开始游戏 begin.countDown(); // 等待end变为0,即所有选手到达终点 end.await(); System.out.println("Game Over"); exec.shutdown(); } }
程序运行结果如下:
Game Start No.10 arrived No.5 arrived No.8 arrived No.9 arrived No.1 arrived No.2 arrived No.3 arrived No.6 arrived No.4 arrived No.7 arrived Game Over
程序执行原理和前一个例子相同。
CountDownLatch工作原理相对简单,可以简单看成一个倒计时器,在构造方法中指定初始值,每次调用countDown()方法时讲计数器减1,而await()会等待计数器变为0。CountDownLatch关键接口如下
countDown() 如果当前计数器的值大于1,则将其减1;若当前值为1,则将其置为0并唤醒所有通过await等待的线程;若当前值为0,则什么也不做直接返回。
await() 等待计数器的值为0,若计数器的值为0则该方法返回;若等待期间该线程被中断,则抛出InterruptedException并清除该线程的中断状态。
await(long timeout, TimeUnit unit) 在指定的时间内等待计数器的值为0,若在指定时间内计数器的值变为0,则该方法返回true;若指定时间内计数器的值仍未变为0,则返回false;若指定时间内计数器的值变为0之前当前线程被中断,则抛出InterruptedException并清除该线程的中断状态。
getCount() 读取当前计数器的值,一般用于调试或者测试。
一个完整的比赛流程:
public class MyThread extends Thread { private static final int THREAD_NUM = 10; private CountDownLatch comingTag; private CountDownLatch waitTag; private CountDownLatch waitRunTag; private CountDownLatch beginTag; private CountDownLatch endTag; public MyThread(CountDownLatch comingTag, CountDownLatch waitTag, CountDownLatch waitRunTag, CountDownLatch beginTag, CountDownLatch endTag) { super(); this.comingTag = comingTag; this.waitTag = waitTag; this.waitRunTag = waitRunTag; this.beginTag = beginTag; this.endTag = endTag; } @Override public void run() { try { System.out.println(Thread.currentThread().getName() + "运动员正在赶往起点。。。"); Thread.sleep((int)(Math.random() * 10000)); comingTag.countDown(); System.out.println(Thread.currentThread().getName() + "等待裁判说准备。。。"); waitTag.await(); System.out.println(Thread.currentThread().getName() + "预备。。。"); Thread.sleep((int)(Math.random() * 10000)); waitRunTag.countDown(); beginTag.await(); System.out.println(Thread.currentThread().getName() + "起跑。。。" + System.currentTimeMillis()); Thread.sleep((int)(Math.random() * 10000)); System.out.println(Thread.currentThread().getName() + "到达终点。。。"); endTag.countDown(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) { try { CountDownLatch comingTag = new CountDownLatch(THREAD_NUM); CountDownLatch waitTag = new CountDownLatch(1); CountDownLatch waitRunTag = new CountDownLatch(THREAD_NUM); CountDownLatch beginTag = new CountDownLatch(1); CountDownLatch endTag = new CountDownLatch(THREAD_NUM); MyThread[] threads = new MyThread[THREAD_NUM]; for (int i = 0; i < threads.length; i++) { threads[i] = new MyThread(comingTag, waitTag, waitRunTag, beginTag, endTag); threads[i].start(); } System.out.println("裁判员正在等待选手到场。。。"); comingTag.await(); System.out.println("裁判员看到全部运动员已到场。。。准备5秒。。。"); Thread.sleep(5000); waitTag.countDown(); System.out.println("各就各位,预备。。。"); waitRunTag.await(); System.out.println("发令枪响起。。。"); beginTag.countDown(); endTag.await(); System.out.println("所有运动员到达终点,比赛结束。。。"); } catch (Exception e) { e.printStackTrace(); } } }
程序运行结果如下:
Thread-0运动员正在赶往起点。。。 Thread-4运动员正在赶往起点。。。 Thread-3运动员正在赶往起点。。。 Thread-2运动员正在赶往起点。。。 Thread-1运动员正在赶往起点。。。 Thread-6运动员正在赶往起点。。。 Thread-5运动员正在赶往起点。。。 Thread-7运动员正在赶往起点。。。 Thread-8运动员正在赶往起点。。。 裁判员正在等待选手到场。。。 Thread-9运动员正在赶往起点。。。 Thread-0等待裁判说准备。。。 Thread-5等待裁判说准备。。。 Thread-3等待裁判说准备。。。 Thread-7等待裁判说准备。。。 Thread-1等待裁判说准备。。。 Thread-6等待裁判说准备。。。 Thread-4等待裁判说准备。。。 Thread-9等待裁判说准备。。。 Thread-8等待裁判说准备。。。 Thread-2等待裁判说准备。。。 裁判员看到全部运动员已到场。。。准备5秒。。。 各就各位,预备。。。 Thread-0预备。。。 Thread-5预备。。。 Thread-3预备。。。 Thread-7预备。。。 Thread-4预备。。。 Thread-8预备。。。 Thread-6预备。。。 Thread-1预备。。。 Thread-2预备。。。 Thread-9预备。。。 发令枪响起。。。 Thread-4起跑。。。1469453409388 Thread-5起跑。。。1469453409388 Thread-6起跑。。。1469453409388 Thread-7起跑。。。1469453409388 Thread-2起跑。。。1469453409388 Thread-9起跑。。。1469453409388 Thread-0起跑。。。1469453409388 Thread-1起跑。。。1469453409388 Thread-8起跑。。。1469453409388 Thread-3起跑。。。1469453409388 Thread-6到达终点。。。 Thread-5到达终点。。。 Thread-2到达终点。。。 Thread-1到达终点。。。 Thread-3到达终点。。。 Thread-0到达终点。。。 Thread-9到达终点。。。 Thread-7到达终点。。。 Thread-8到达终点。。。 Thread-4到达终点。。。 所有运动员到达终点,比赛结束。。。
相关文章推荐
- Java并发编程核心方法与框架-exchanger的使用
- 尚学堂 JAVA DAY12 概念总结
- Java并发编程核心方法与框架-Semaphore的使用
- 安装Eclipse并配置JacORB插件
- Spring Boot实战之入门
- RxJava中的Subject和常见的生命周期管理
- java 一个函数如何返回多个值
- 读取文件内容并排序
- 尚学堂 JAVA DAY12 java程序执行时内存的分配
- JavaWEB小知识学习--验证码生成
- 尚学堂 JAVA DAY11 概念总结
- Java基本数据类型及数据类型转换
- Spring4 MVC文件下载实例
- Spring MVC实现文件下载
- java实现选择排序
- 如何去掉MyEclipse中的MyEclipse Derby
- Maven学习笔记(三)——使用Maven构件web项目
- JAVA 利用Throwable和Thread分析堆栈跟踪元素
- Java 数据保存与读取,保存数据信息并加密
- 第2章 Java内存区域与内存溢出异常