java等待线程结束_java中等待所有线程都执行结束(转)

看过之后在想java中有很大的灵活性,应该有更多的方式可以做这件事。

这个事情的场景是这样的:许多线程并行的计算一堆问题,然后每个计算存在一个队列,在主线程要等待所有计算结果完成后排序并展示出来。这样的问题其实很常见。

1. 使用join。这种方式其实并不是那么的优雅,将所有线程启动完之后还需要将所有线程都join,但是每次join都会阻塞,直到被join线程完成,很可能所有被阻塞线程已经完事了,主线程还在不断地join,貌似有点浪费,而且两个循环也不太好看。

1 public voidtestThreadSync1() {2

3 final Vector list = new Vector();4 Thread[] threads = newThread[TEST_THREAD_COUNT];5 try{6 for (int i = 0; i < TEST_THREAD_COUNT; i++) {7 final int num =i;8 threads[i] = new Thread(newRunnable() {9 public voidrun() {10 try{11 Thread.sleep(random.nextInt(100));12 } catch(InterruptedException e) {13 e.printStackTrace();14 }15 list.add(num);16 System.out.print(num + " add.\t");17 }18 });19 threads[i].start();20 }21 for (int i = 0; i < threads.length; i++) {22 threads[i].join();23 System.out.print(i + " end.\t");24 }25 } catch(InterruptedException ie) {26 ie.printStackTrace();27 }28 printSortedResult(list);29 }

1 9 add. 7 add. 3 add. 5 add. 4 add. 1 add. 0 add. 0 end. 1 end. 8 add. 2 add. 2 end. 3 end. 4 end. 5 end. 6 add. 6 end. 7 end. 8 end. 9end.2 before sort3 9 7 3 5 4 1 0 8 2 6

4 after sort5 0 1 2 3 4 5 6 7 8 9

2. 使用wait/notifyAll,这个方式其实跟上面是类似的,只是比较底层些吧(join实际上也是wait)。

1 @Test2 public void testThreadSync2() throwsIOException, InterruptedException {3 final Object waitObject = newObject();4 final AtomicInteger count = newAtomicInteger(TEST_THREAD_COUNT);5 final Vector list = new Vector();6 Thread[] threads = newThread[TEST_THREAD_COUNT];7 for (int i = 0; i < TEST_THREAD_COUNT; i++) {8 final int num =i;9 threads[i] = new Thread(newRunnable() {10 public voidrun() {11 try{12 Thread.sleep(random.nextInt(100));13 } catch(InterruptedException e) {14 e.printStackTrace();15 }16 list.add(num);17 System.out.print(num + " add.\t");18 synchronized(waitObject) {19 int cnt =count.decrementAndGet();20 if (cnt == 0) {21 waitObject.notifyAll();22 }23 }24 }25 });26 threads[i].start();27 }28 synchronized(waitObject) {29 while (count.get() != 0) {30 waitObject.wait();31 }32 }33 printSortedResult(list);34 }

3. 使用CountDownLatch,这其实是最优雅的写法了,每个线程完成后都去将计数器减一,最后完成时再来唤醒。

例1

1 @Test2 public voidtestThreadSync3() {3 final Vector list = new Vector();4 Thread[] threads = newThread[TEST_THREAD_COUNT];5 final CountDownLatch latch = newCountDownLatch(TEST_THREAD_COUNT);6 for (int i = 0; i < TEST_THREAD_COUNT; i++) {7 final int num =i;8 threads[i] = new Thread(newRunnable() {9 public voidrun() {10 try{11 Thread.sleep(random.nextInt(100));12 } catch(InterruptedException e) {13 e.printStackTrace();14 }15 list.add(num);16 System.out.print(num + " add.\t");17 latch.countDown();18 }19 });20 threads[i].start();21 }22 try{23 latch.await();24 } catch(InterruptedException e) {25 e.printStackTrace();26 }27 printSortedResult(list);28 }

例2

CountDownLatch 初始化设置count,即等待(await)count个线程或一个线程count次计数,通过工作线程来countDown计数减一,直到计数为0,await阻塞结束。

设置的count不可更改,如需要动态设置计数的线程数,可以使用CyclicBarrier.

下面的例子,所有的工作线程中准备就绪以后,并不是直接运行,而是等待主线程的信号后再执行具体的操作。

1 packagecom.example.multithread;2

3 importjava.util.concurrent.CountDownLatch;4

5 classDriver6 {7 private static final int TOTAL_THREADS = 10;8 private final CountDownLatch mStartSignal = new CountDownLatch(1);9 private final CountDownLatch mDoneSignal = newCountDownLatch(TOTAL_THREADS);10

11 voidmain()12 {13 for (int i = 0; i < TOTAL_THREADS; i++)14 {15 new Thread(newWorker(mStartSignal, mDoneSignal, i)).start();16 }17 System.out.println("Main Thread Now:" +System.currentTimeMillis());18 doPrepareWork();//准备工作

19 mStartSignal.countDown();//计数减一为0,工作线程真正启动具体操作

20 doSomethingElse();//做点自己的事情

21 try

22 {23 mDoneSignal.await();//等待所有工作线程结束

24 }25 catch(InterruptedException e)26 {27 //TODO Auto-generated catch block

28 e.printStackTrace();29 }30 System.out.println("All workers have finished now.");31 System.out.println("Main Thread Now:" +System.currentTimeMillis());32 }33

34 voiddoPrepareWork()35 {36 System.out.println("Ready,GO!");37 }38

39 voiddoSomethingElse()40 {41 for (int i = 0; i < 100000; i++)42 {43 ;//delay

44 }45 System.out.println("Main Thread Do something else.");46 }47 }48

49 class Worker implementsRunnable50 {51 private finalCountDownLatch mStartSignal;52 private finalCountDownLatch mDoneSignal;53 private final intmThreadIndex;54

55 Worker(final CountDownLatch startSignal, finalCountDownLatch doneSignal,56 final intthreadIndex)57 {58 this.mDoneSignal =doneSignal;59 this.mStartSignal =startSignal;60 this.mThreadIndex =threadIndex;61 }62

63 @Override64 public voidrun()65 {66 //TODO Auto-generated method stub

67 try

68 {69 mStartSignal.await();//阻塞,等待mStartSignal计数为0运行后面的代码70 //所有的工作线程都在等待同一个启动的命令

71 doWork();//具体操作

72 System.out.println("Thread " + mThreadIndex + " Done Now:"

73 +System.currentTimeMillis());74 mDoneSignal.countDown();//完成以后计数减一

75 }76 catch(InterruptedException e)77 {78 //TODO Auto-generated catch block

79 e.printStackTrace();80 }81 }82

83 public voiddoWork()84 {85 for (int i = 0; i < 1000000; i++)86 {87 ;//耗时操作

88 }89 System.out.println("Thread " + mThreadIndex + ":do work");90 }91 }92

93 public classCountDownLatchTest94 {95 public static voidmain(String[] args)96 {97 //TODO Auto-generated method stub

98 newDriver().main();99 }100

101 }

通过Executor启动线程:

1 classCountDownLatchDriver22 {3 private static final int TOTAL_THREADS = 10;4 private final CountDownLatch mDoneSignal = newCountDownLatch(TOTAL_THREADS);5

6

7

8 voidmain()9 {10 System.out.println("Main Thread Now:" +System.currentTimeMillis());11 doPrepareWork();//准备工作

12

13 Executor executor =Executors.newFixedThreadPool(TOTAL_THREADS);14 for (int i = 0; i < TOTAL_THREADS; i++)15 {16 //通过内建的线程池维护创建的线程

17 executor.execute(newRunnableWorker(mDoneSignal, i));18 }19 doSomethingElse();//做点自己的事情

20 try

21 {22 mDoneSignal.await();//等待所有工作线程结束

23 }24 catch(InterruptedException e)25 {26 //TODO Auto-generated catch block

27 e.printStackTrace();28 }29 System.out.println("All workers have finished now.");30 System.out.println("Main Thread Now:" +System.currentTimeMillis());31 }32

33 voiddoPrepareWork()34 {35 System.out.println("Ready,GO!");36 }37

38 voiddoSomethingElse()39 {40 for (int i = 0; i < 100000; i++)41 {42 ;//delay

43 }44 System.out.println("Main Thread Do something else.");45 }46 }47

48 class RunnableWorker implementsRunnable49 {50

51 private finalCountDownLatch mDoneSignal;52 private final intmThreadIndex;53

54 RunnableWorker(final CountDownLatch doneSignal, final intthreadIndex)55 {56 this.mDoneSignal =doneSignal;57 this.mThreadIndex =threadIndex;58 }59

60 @Override61 public voidrun()62 {63 //TODO Auto-generated method stub

64

65 doWork();//具体操作

66 System.out.println("Thread " + mThreadIndex + " Done Now:"

67 +System.currentTimeMillis());68 mDoneSignal.countDown();//完成以后计数减一69 //计数为0时,主线程接触阻塞,继续执行其他任务

70 try

71 {72 //可以继续做点其他的事情,与主线程无关了

73 Thread.sleep(5000);74 System.out.println("Thread " +mThreadIndex75 + " Do something else after notifing main thread");76

77 }78 catch(InterruptedException e)79 {80 //TODO Auto-generated catch block

81 e.printStackTrace();82 }83

84 }85

86 public voiddoWork()87 {88 for (int i = 0; i < 1000000; i++)89 {90 ;//耗时操作

91 }92 System.out.println("Thread " + mThreadIndex + ":do work");93 }94 }

输出:

1 Main Thread Now:1359959480786

2 Ready,GO!

3 Thread 0:dowork4 Thread 0 Done Now:1359959480808

5 Thread 1:dowork6 Thread 1 Done Now:1359959480811

7 Thread 2:dowork8 Thread 2 Done Now:1359959480813

9 Main Thread Do something else.10 Thread 3:dowork11 Thread 3 Done Now:1359959480825

12 Thread 5:dowork13 Thread 5 Done Now:1359959480827

14 Thread 7:dowork15 Thread 7 Done Now:1359959480829

16 Thread 9:dowork17 Thread 9 Done Now:1359959480831

18 Thread 4:dowork19 Thread 4 Done Now:1359959480833

20 Thread 6:dowork21 Thread 6 Done Now:1359959480835

22 Thread 8:dowork23 Thread 8 Done Now:1359959480837

24 All workers have finished now.25 Main Thread Now:1359959480838

26 Thread 0 Do something elseafter notifing main thread27 Thread 1 Do something elseafter notifing main thread28 Thread 2 Do something elseafter notifing main thread29 Thread 3 Do something elseafter notifing main thread30 Thread 9 Do something elseafter notifing main thread31 Thread 7 Do something elseafter notifing main thread32 Thread 5 Do something elseafter notifing main thread33 Thread 4 Do something elseafter notifing main thread34 Thread 6 Do something elseafter notifing main thread35 Thread 8 Do something else after notifing main thread

4. 使用CyclicBarrier。这里其实类似上面,这个berrier只是在等待完成后自动调用传入CyclicBarrier的Runnable。

例1

1 @Test2 public void testThreadSync4() throwsIOException {3 final Vector list = new Vector();4 Thread[] threads = newThread[TEST_THREAD_COUNT];5 final CyclicBarrier barrier = newCyclicBarrier(TEST_THREAD_COUNT,6 newRunnable() {7 public voidrun() {8 printSortedResult(list);9 }10 });11 for (int i = 0; i < TEST_THREAD_COUNT; i++) {12 final int num =i;13 threads[i] = new Thread(newRunnable() {14 public voidrun() {15 try{16 Thread.sleep(random.nextInt(100));17 } catch(InterruptedException e) {18 e.printStackTrace();19 }20 list.add(num);21 System.out.print(num + " add.\t");22 try{23 barrier.await();24 } catch(InterruptedException e) {25 e.printStackTrace();26 } catch(BrokenBarrierException e) {27 e.printStackTrace();28 }29 }30 });31 threads[i].start();32 }33 System.in.read();34 }

例2

1 classWalkTarget2 {3 private final int mCount = 5;4 private finalCyclicBarrier mBarrier;5 ExecutorService mExecutor;6

7 class BarrierAction implementsRunnable8 {9 @Override10 public voidrun()11 {12 //TODO Auto-generated method stub

13 System.out.println("所有线程都已经完成任务,计数达到预设值");14 //mBarrier.reset();//恢复到初始化状态

15

16 }17 }18

19 WalkTarget()20 {21 //初始化CyclicBarrier

22 mBarrier = new CyclicBarrier(mCount, newBarrierAction());23 mExecutor =Executors.newFixedThreadPool(mCount);24

25 for (int i = 0; i < mCount; i++)26 {27 //启动工作线程

28 mExecutor.execute(newWalker(mBarrier, i));29 }30 }31 }32

33 //工作线程

34 class Walker implementsRunnable35 {36 private finalCyclicBarrier mBarrier;37 private final intmThreadIndex;38

39 Walker(final CyclicBarrier barrier, final intthreadIndex)40

41

42 {43 mBarrier =barrier;44 mThreadIndex =threadIndex;45 }46

47 @Override48 public voidrun()49 {50 //TODO Auto-generated method stub

51 System.out.println("Thread " + mThreadIndex + " is running...");52 //执行任务

53 try

54 {55 TimeUnit.MILLISECONDS.sleep(5000);56 //do task

57 }58 catch(InterruptedException e)59 {60 //TODO Auto-generated catch block

61 e.printStackTrace();62 }63

64 //完成任务以后,等待其他线程完成任务

65 try

66 {67 mBarrier.await();68 }69 catch(InterruptedException e)70 {71 //TODO Auto-generated catch block

72 e.printStackTrace();73 }74 catch(BrokenBarrierException e)75 {76 //TODO Auto-generated catch block

77 e.printStackTrace();78 }79 //其他线程任务都完成以后,阻塞解除,可以继续接下来的任务

80 System.out.println("Thread " + mThreadIndex + " do something else");81 }82

83 }84

85 public classCountDownLatchTest86 {87 public static voidmain(String[] args)88 {89 //TODO Auto-generated method stub90 //new CountDownLatchDriver2().main();

91 newWalkTarget();92 }93

94 }

输出(注意,只有所有的线程barrier.await之后才能继续执行其他的操作):

Thread 0 is running... Thread 2 is running... Thread 3 is running... Thread 1 is running... Thread 4 is running... 所有线程都已经完成任务,计数达到预设值 Thread 4 do something else Thread 0 do something else Thread 2 do something else Thread 3 do something else Thread 1 do something else

5、

CountDownLatch和CyclicBarrier简单比较:

CountDownLatchCyclicBarrier

软件包

java.util.concurrent

java.util.concurrent

适用情景

主线程等待多个工作线程结束

多个线程之间互相等待,直到所有线程达到一个障碍点(Barrier point)

主要方法

CountDownLatch(int count) (主线程调用)

初始化计数

CountDownLatch.await (主线程调用)

阻塞,直到等待计数为0解除阻塞

CountDownLatch.countDown

计数减一(工作线程调用)

CyclicBarrier(int parties, Runnable barrierAction) //初始化参与者数量和障碍点执行Action,Action可选。由主线程初始化

CyclicBarrier.await() //由参与者调用

阻塞,直到所有线程达到屏障点

等待结束

各线程之间不再互相影响,可以继续做自己的事情。不再执行下一个目标工作。

在屏障点达到后,允许所有线程继续执行,达到下一个目标。可以重复使用CyclicBarrier

异常

如果其中一个线程由于中断,错误,或超时导致永久离开屏障点,其他线程也将抛出异常。

其他

如果BarrierAction不依赖于任何Party中的所有线程,那么在任何party中的一个线程被释放的时候,可以直接运行这个Action。

If(barrier.await()==2)

{

//do action

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值