Java中CountDownLatch、CyclicBarrier、Thread.join方法基本应用
2015-11-19 19:23
531 查看
在多线程程序设计中,经常会遇到一个线程等待一个或多个线程的场景,遇到这样的场景应该如何解决?
如果是一个线程等待一个线程,则可以通过await()和notify()来实现;
如果是一个线程等待多个线程,则就可以使用CountDownLatch和CyclicBarrier来实现比较好的控制。
(1)CyclicBarrier
需要所有的子任务都完成时,才执行主任务,这个时候就可以选择使用CyclicBarrier。
运行结果:
可以看到await()的作用就是当所有的线程都运行到这个地方的时候,才能继续往下执行。
(2)CountDownLatch
CountDownLatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countDown()方法,计数器减1,计数器大于0 时,await()方法会阻塞程序继续执行。
运行结果如下:
可以看到CyclicBarrier执行await方法之后,所有线程运行到这个地方会自动阻塞,当所有线程都到达之后,所有线程会自动向下接着运行。
CountDownLatch执行await后会查看计数器,如果计数器大于0就会阻塞,当执行coutDown之后,计数器就会减1,如果计数器不大于0,这样之前阻塞的线程就会进行执行,也就是说CountDownLatch是可控的。
(3)Thread.join()
Thread类中有一个join()方法,在一个线程中启动另外一个线程的join方法,当前线程将会挂起,而执行被启动的线程,知道被启动的线程执行完毕后,当前线程才开始执行。join方法定义在Thread类中,则调用者必须是一个线程。
t1启动后,调用join()方法,直到t1的计数任务结束,才轮到t2启动,然后t2也开始计数任务。可以看到,实例中,两个线程就按着严格的顺序来执行了。
参考文章:
http://blog.csdn.net/xieyuooo/article/details/8572543
如果是一个线程等待一个线程,则可以通过await()和notify()来实现;
如果是一个线程等待多个线程,则就可以使用CountDownLatch和CyclicBarrier来实现比较好的控制。
(1)CyclicBarrier
需要所有的子任务都完成时,才执行主任务,这个时候就可以选择使用CyclicBarrier。
import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; public class BarrierTest { private static final int THREAD_COUNT = 10; private final static CyclicBarrier CYCLIC_BARRIER = new CyclicBarrier(THREAD_COUNT , new Runnable() { public void run() { System.out.println("======>我是导游,本次点名结束,准备走下一个环节!"); } } ); public static void main(String []args) throws InterruptedException, BrokenBarrierException { for(int i = 0 ; i < 10 ; i++) { new Thread(String.valueOf(i)) { public void run() { try { System.out.println("我是线程:" + this.getName() + " 我们达到旅游地点!"); CYCLIC_BARRIER.await(); System.out.println("我是线程:" + this.getName() + " 我开始骑车!"); CYCLIC_BARRIER.await(); System.out.println("我是线程:" + this.getName() + " 我们开始爬山!"); CYCLIC_BARRIER.await(); System.out.println("我是线程:" + this.getName() + " 我们回宾馆休息!"); CYCLIC_BARRIER.await(); System.out.println("我是线程:" + this.getName() + " 我们开始乘车回家!"); CYCLIC_BARRIER.await(); System.out.println("我是线程:" + this.getName() + " 我们到家了!"); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } } }.start(); } } }
运行结果:
我是线程:0 我们达到旅游地点! 我是线程:1 我们达到旅游地点! 我是线程:2 我们达到旅游地点! 我是线程:3 我们达到旅游地点! 我是线程:4 我们达到旅游地点! 我是线程:5 我们达到旅游地点! 我是线程:6 我们达到旅游地点! 我是线程:7 我们达到旅游地点! 我是线程:8 我们达到旅游地点! 我是线程:9 我们达到旅游地点! ======>我是导游,本次点名结束,准备走下一个环节! 我是线程:9 我开始骑车! 我是线程:0 我开始骑车! 我是线程:1 我开始骑车! 我是线程:2 我开始骑车! 我是线程:4 我开始骑车! 我是线程:3 我开始骑车! 我是线程:7 我开始骑车! 我是线程:8 我开始骑车! 我是线程:6 我开始骑车! 我是线程:5 我开始骑车! ======>我是导游,本次点名结束,准备走下一个环节! 我是线程:6 我们开始爬山! 我是线程:5 我们开始爬山! 我是线程:7 我们开始爬山! 我是线程:8 我们开始爬山! 我是线程:3 我们开始爬山! 我是线程:4 我们开始爬山! 我是线程:2 我们开始爬山! 我是线程:1 我们开始爬山! 我是线程:0 我们开始爬山! 我是线程:9 我们开始爬山! ======>我是导游,本次点名结束,准备走下一个环节! 我是线程:5 我们回宾馆休息! 我是线程:3 我们回宾馆休息! 我是线程:7 我们回宾馆休息! 我是线程:8 我们回宾馆休息! 我是线程:6 我们回宾馆休息! 我是线程:4 我们回宾馆休息! 我是线程:0 我们回宾馆休息! 我是线程:1 我们回宾馆休息! 我是线程:2 我们回宾馆休息! 我是线程:9 我们回宾馆休息! ======>我是导游,本次点名结束,准备走下一个环节! 我是线程:9 我们开始乘车回家! 我是线程:2 我们开始乘车回家! 我是线程:4 我们开始乘车回家! 我是线程:1 我们开始乘车回家! 我是线程:0 我们开始乘车回家! 我是线程:6 我们开始乘车回家! 我是线程:7 我们开始乘车回家! 我是线程:8 我们开始乘车回家! 我是线程:3 我们开始乘车回家! 我是线程:5 我们开始乘车回家! ======>我是导游,本次点名结束,准备走下一个环节! 我是线程:3 我们到家了! 我是线程:0 我们到家了! 我是线程:1 我们到家了! 我是线程:4 我们到家了! 我是线程:2 我们到家了! 我是线程:9 我们到家了! 我是线程:6 我们到家了! 我是线程:7 我们到家了! 我是线程:5 我们到家了! 我是线程:8 我们到家了!
可以看到await()的作用就是当所有的线程都运行到这个地方的时候,才能继续往下执行。
(2)CountDownLatch
CountDownLatch类是一个同步计数器,构造时传入int参数,该参数就是计数器的初始值,每调用一次countDown()方法,计数器减1,计数器大于0 时,await()方法会阻塞程序继续执行。
import java.util.concurrent.CountDownLatch; public class CountDownLatchTest { private final static int GROUP_SIZE = 5; public static void main(String []args) { final CountDownLatch start_count_down = new CountDownLatch(1); System.out.println("==========================>\n比赛开始:"); for(int i = 0 ; i < GROUP_SIZE ; i++) { new Thread(String.valueOf(i)) { public void run() { System.out.println("第:" + this.getName() + " 号线程,我已经准备就绪!"); try { start_count_down.await();// 计数器大于0,线程等待 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("第:" + this.getName() + " 号线程,我已执行完成!"); } }.start(); } try { Thread.sleep(1000); // 一秒之后,等所有线程准备就绪就开始 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("各就各位,预备!"); start_count_down.countDown();// 计数器减1,变成了0,线程停止等待 } }
运行结果如下:
==========================> 比赛开始: 第:0 号线程,我已经准备就绪! 第:1 号线程,我已经准备就绪! 第:2 号线程,我已经准备就绪! 第:3 号线程,我已经准备就绪! 第:4 号线程,我已经准备就绪! 各就各位,预备! 第:0 号线程,我已执行完成! 第:2 号线程,我已执行完成! 第:1 号线程,我已执行完成! 第:4 号线程,我已执行完成! 第:3 号线程,我已执行完成!
可以看到CyclicBarrier执行await方法之后,所有线程运行到这个地方会自动阻塞,当所有线程都到达之后,所有线程会自动向下接着运行。
CountDownLatch执行await后会查看计数器,如果计数器大于0就会阻塞,当执行coutDown之后,计数器就会减1,如果计数器不大于0,这样之前阻塞的线程就会进行执行,也就是说CountDownLatch是可控的。
(3)Thread.join()
Thread类中有一个join()方法,在一个线程中启动另外一个线程的join方法,当前线程将会挂起,而执行被启动的线程,知道被启动的线程执行完毕后,当前线程才开始执行。join方法定义在Thread类中,则调用者必须是一个线程。
class ThreadTesterA implements Runnable { private int counter; @Override public void run() { while (counter <= 10) { System.out.print("Counter = " + counter + " "); counter++; } System.out.println(); } } class ThreadTesterB implements Runnable { private int i; @Override public void run() { while (i <= 10) { System.out.print("i = " + i + " "); i++; } System.out.println(); } } public class ThreadTester { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(new ThreadTesterA()); Thread t2 = new Thread(new ThreadTesterB()); t1.start(); t1.join(); // wait t1 to be finished t2.start(); t2.join(); // in this program, this may be removed } }
t1启动后,调用join()方法,直到t1的计数任务结束,才轮到t2启动,然后t2也开始计数任务。可以看到,实例中,两个线程就按着严格的顺序来执行了。
参考文章:
http://blog.csdn.net/xieyuooo/article/details/8572543
相关文章推荐
- Jersey中Json与Java对象转换处理
- 解决:MyEclipse中SVN更新引起代码冲突导致部署失败
- java题目-1:输入20个学生的成绩,算出算出平均分和低于平均分的人数
- 几种 Java 序列化方案的性能比较
- Java 异常处理机制(一)
- highcharts+java
- 堆栈和队列的java实现
- java调用不在同一目录下的bat
- Spring并发访问的线程安全性问题
- javaweb学习总结(二十五)——jsp简单标签开发(一)
- java学习笔记03
- 简单项目开发之一(springmvc+log4j)
- spring里面怎么取容器里面的常量值
- java中的TreeSet集合
- 简单项目开发之一(springmvc+filter)
- Eclipse常见错误
- java学习笔记2
- MyEclipse 2015 stable 2.0 下载 破解激活
- java二分查找方法
- struts框架设计