用 join 或 CountDownLatch 让主线程等待所有子线程完成
原文出处: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();完成了主线程阻塞。简洁就是好,以后就这种方式了。