原文出处: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。
- packagecom.chenlb;
- importjava.util.Random;
- /**
- *@authorchenlb2008-11-1下午11:32:43
- */
- publicclassWaitAllSubThread{
- /*intliveThreadNum;//记录运行的子线程数
- */
- intn;//工作线程数
- publicWaitAllSubThread(intn){
- this.n=n;
- }
- classWorkerimplementsRunnable{
- Stringname;
- intsleep;
- publicWorker(Stringname,intsleep){
- this.name=name;
- this.sleep=sleep;
- }
- publicvoidrun(){
- /*upLive();//计算此线程已经工作.
- */
- System.out.println(name+",starttowork.");
- try{
- Thread.sleep(sleep);//虚拟工作.10s随机时间
- }catch(InterruptedExceptione){
- System.out.println(name+"interrupted.");
- }
- System.out.println(name+",endtowork["+sleep+"]sleep.");
- /*downLive();//此线程工作完成
- */
- }
- }
- /*//记录线程数的同步方法.
- privatesynchronizedvoiddownLive(){
- liveThreadNum--;
- }
- privatesynchronizedvoidupLive(){
- liveThreadNum++;
- }
- privatesynchronizedbooleanisLive(){
- returnliveThreadNum>0;
- }*/
- publicvoidrun(){
- System.out.println("-------------mainrunstart-------------");
- intsleepSaid=10*1000;//每个工作线程虚拟工作最大时间
- Randomrm=newRandom();
- for(inti=0;i<ths.length;i++){
- ths[i]=newThread(newMyTask(rm.nextInt(sleep)+1));
- ths[i].start();
- }
- for(Threadth:ths){
- try{
- th.join();//join方式
- }catch(InterruptedExceptione){
- //TODOAuto-generatedcatchblock
- e.printStackTrace();
- }
- }
- /*//等待所有工作线程完成.
- while(isLive()){
- try{
- Thread.sleep(1000);//每隔1s查看下是否所有线程完成.
- }catch(InterruptedExceptione){
- System.out.println("mainthreadsleepinterrupted.");
- }
- }*/
- System.out.println("---------------mainrunend--------------");
- }
- publicstaticvoidmain(String[]args){
- WaitAllSubThreadwast=newWaitAllSubThread(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。
- packagecom.chenlb;
- importjava.util.Random;
- importjava.util.concurrent.CountDownLatch;
- /**
- *@authorchenlb2008-11-1下午11:43:31
- */
- publicclassCountDownLatchUse{
- finalCountDownLatchdownLatch;
- intn;//工作线程数
- publicCountDownLatchUse(intn){
- this.downLatch=newCountDownLatch(n);
- this.n=n;
- }
- classWorkerimplementsRunnable{
- Stringname;
- intsleep;
- publicWorker(Stringname,intsleep){
- this.name=name;
- this.sleep=sleep;
- }
- publicvoidrun(){
- System.out.println(name+",starttowork.");
- try{
- Thread.sleep(sleep);//虚拟工作.10s随机时间
- }catch(InterruptedExceptione){
- System.out.println(name+"interrupted.");
- }
- System.out.println(name+",endtowork["+sleep+"]sleep.");
- meDone();//某个工作线程完成
- }
- }
- privatevoidmeDone(){
- downLatch.countDown();
- }
- publicvoidrun(){
- System.out.println("-------------mainrunstart-------------");
- intsleepSaid=10*1000;//每个工作线程虚拟工作最大时间
- Randomrm=newRandom();
- for(inti=0;i<n;i++){
- newThread(newWorker("worker-"+i,rm.nextInt(sleepSaid)+1)).start();
- }
- try{
- downLatch.await();//等待所有工作线程完成.
- }catch(InterruptedExceptione){
- System.out.println("maininterrupted.");
- }
- System.out.println("---------------mainrunend--------------");
- }
- publicstaticvoidmain(String[]args){
- CountDownLatchUsemtu=newCountDownLatchUse(10);
- mtu.run();
- }
- }
CountDownLatch.countDown();完成线程的计数。CountDownLatch.await();完成了主线程阻塞。简洁就是好,以后就这种方式了。