原文出处:http://blog.chenlb.com/2008/11/join-or-countdownlatch-make-main-thread-wait-all-sub-thread.html
在编写多线程的工作中,有个常见的问题:主线程(main) 启动好几个子线程(task)来完成并发任务,主线程要等待所有的子线程完成之后才继续执行main的其它任务。
默认主线程退出时其它子线程不会停,如果想让main退出时其它子线程终止,可以用subThread.setDaemon(true) 设置子线程为“守护线程”。但现在要的是主线程等待所有子线程完成后,还要执行其它操作(比如:结果合并)。记得可以用join()方法来等待所有子线程 完成后,才继续执行。如果不用join(),main线程与子线程是并发的,要稍加处理使main线程暂停。简单点用Thread.sleep(long millis) 了,当然用“等待-通知”机制也可以。
下面是用join的实现main等待所有子线程完成了,示例代码:WaitAllSubThread.java。
- package com.chenlb;
- import java.util.Random;
- /**
- * @author chenlb 2008-11-1 下午11:32:43
- */
- public class WaitAllSubThread {
- /*int liveThreadNum;//记录运行的子线程数
- */
- int n; //工作线程数
- public WaitAllSubThread(int n) {
- this.n = n;
- }
- class Worker implements Runnable {
- String name;
- int sleep;
- public Worker(String name, int sleep) {
- this.name = name;
- this.sleep = sleep;
- }
- public void run() {
- /*upLive(); //计算此线程已经工作.
- */
- System.out.println(name+", start to work.");
- try {
- Thread.sleep(sleep); //虚拟工作. 10s 随机时间
- } catch (InterruptedException e) {
- System.out.println(name+" interrupted.");
- }
- System.out.println(name+", end to work ["+sleep+"] sleep.");
- /*downLive(); //此线程工作完成
- */
- }
- }
- /* //记录线程数的同步方法.
- private synchronized void downLive() {
- liveThreadNum--;
- }
- private synchronized void upLive() {
- liveThreadNum++;
- }
- private synchronized boolean isLive() {
- return liveThreadNum > 0;
- }*/
- public void run() {
- System.out.println("-------------main run start-------------");
- int sleepSaid = 10 * 1000; //每个工作线程虚拟工作最大时间
- Random rm = new Random();
- for(int i=0; i<ths.length; i++) {
- ths[i] = new Thread(new MyTask(rm.nextInt(sleep)+1));
- ths[i].start();
- }
- for(Thread th : ths) {
- try {
- th.join();//join方式
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- /*//等待所有工作线程完成.
- while(isLive()) {
- try {
- Thread.sleep(1000); //每隔1s查看下是否所有线程完成.
- } catch (InterruptedException e) {
- System.out.println("main thread sleep interrupted.");
- }
- }*/
- System.out.println("---------------main run end--------------");
- }
- public static void main(String[] args) {
- WaitAllSubThread wast = new WaitAllSubThread(10);
- wast.run();
- }
- }
如果不用join,上面的代码会使先输出“main run end”,原因是:main 与 所有sub thread并发工作,不等待所有子线程继续工作。而所有子线程完成了,main线程才会退出。
用比较笨的方式:把上面/* */的注释去掉,把 th.join();块注释掉。这样可以使等待所有子线程完成了才去执行其它后续的(比如:这里是输出“main run end”)。分析:程序中加工作的子线程的计数(liveThreadNum)。main不断轮询是否所有子线程完成,所有完成就执行剩下的。
上面的是昨天写的,今天发现一个更加简洁的方式去处理main线程阻塞(等待所有子线程),那就是java.util.concurrent.CountDownLatch类。现在重新实现上面的功能,CountDownLatchUse.java。
- package com.chenlb;
- import java.util.Random;
- import java.util.concurrent.CountDownLatch;
- /**
- * @author chenlb 2008-11-1 下午11:43:31
- */
- public class CountDownLatchUse {
- final CountDownLatch downLatch;
- int n; //工作线程数
- public CountDownLatchUse(int n) {
- this.downLatch = new CountDownLatch(n);
- this.n = n;
- }
- class Worker implements Runnable {
- String name;
- int sleep;
- public Worker(String name, int sleep) {
- this.name = name;
- this.sleep = sleep;
- }
- public void run() {
- System.out.println(name+", start to work.");
- try {
- Thread.sleep(sleep); //虚拟工作. 10s 随机时间
- } catch (InterruptedException e) {
- System.out.println(name+" interrupted.");
- }
- System.out.println(name+", end to work ["+sleep+"] sleep.");
- meDone(); //某个工作线程完成
- }
- }
- private void meDone() {
- downLatch.countDown();
- }
- public void run() {
- System.out.println("-------------main run start-------------");
- int sleepSaid = 10 * 1000; //每个工作线程虚拟工作最大时间
- Random rm = new Random();
- for(int i=0; i<n; i++) {
- new Thread(new Worker("worker-"+i, rm.nextInt(sleepSaid)+1)).start();
- }
- try {
- downLatch.await(); //等待所有工作线程完成.
- } catch (InterruptedException e) {
- System.out.println("main interrupted.");
- }
- System.out.println("---------------main run end--------------");
- }
- public static void main(String[] args) {
- CountDownLatchUse mtu = new CountDownLatchUse(10);
- mtu.run();
- }
- }
CountDownLatch.countDown();完成线程的计数。CountDownLatch.await();完成了主线程阻塞。简洁就是好,以后就这种方式了。