转自:http://blog.csdn.net/liweisnake/article/details/12966761

今天看到一篇文章,是关于java中如何等待所有线程都执行结束,文章总结得很好,原文如下http://software.intel.com/zh-cn/blogs/2013/10/15/java-countdownlatchcyclicbarrier/?utm_campaign=CSDN&utm_source=intel.csdn.net&utm_medium=Link&utm_content=others-%20Java

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

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

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

    1. public void testThreadSync1() {
    2.  
    3. final Vector<Integer> list = new Vector<Integer>();
    4. Thread[] threads = new Thread[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(new Runnable() {
    9. public void run() {
    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. 9 end.
    2. before sort
    3. 9 7 3 5 4 1 0 8 2 6
    4. after sort
    5. 0 1 2 3 4 5 6 7 8 9

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

    1. @Test
    2. public void testThreadSync2() throws IOException, InterruptedException {
    3. final Object waitObject = new Object();
    4. final AtomicInteger count = new AtomicInteger(TEST_THREAD_COUNT);
    5. final Vector<Integer> list = new Vector<Integer>();
    6. Thread[] threads = new Thread[TEST_THREAD_COUNT];
    7. for (int i = 0; i < TEST_THREAD_COUNT; i++) {
    8. final int num = i;
    9. threads[i] = new Thread(new Runnable() {
    10. public void run() {
    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. @Test
    2. public void testThreadSync3() {
    3. final Vector<Integer> list = new Vector<Integer>();
    4. Thread[] threads = new Thread[TEST_THREAD_COUNT];
    5. final CountDownLatch latch = new CountDownLatch(TEST_THREAD_COUNT);
    6. for (int i = 0; i < TEST_THREAD_COUNT; i++) {
    7. final int num = i;
    8. threads[i] = new Thread(new Runnable() {
    9. public void run() {
    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. package com.example.multithread;
  2.  
  3. import java.util.concurrent.CountDownLatch;
  4.  
  5. class Driver
  6. {
  7. private static final int TOTAL_THREADS = 10;
  8. private final CountDownLatch mStartSignal = new CountDownLatch(1);
  9. private final CountDownLatch mDoneSignal = new CountDownLatch(TOTAL_THREADS);
  10.  
  11. void main()
  12. {
  13. for (int i = 0; i < TOTAL_THREADS; i++)
  14. {
  15. new Thread(new Worker(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. void doPrepareWork()
  35. {
  36. System.out.println("Ready,GO!");
  37. }
  38.  
  39. void doSomethingElse()
  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 implements Runnable
  50. {
  51. private final CountDownLatch mStartSignal;
  52. private final CountDownLatch mDoneSignal;
  53. private final int mThreadIndex;
  54.  
  55. Worker(final CountDownLatch startSignal, final CountDownLatch doneSignal,
  56. final int threadIndex)
  57. {
  58. this.mDoneSignal = doneSignal;
  59. this.mStartSignal = startSignal;
  60. this.mThreadIndex = threadIndex;
  61. }
  62.  
  63. @Override
  64. public void run()
  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 void doWork()
  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 class CountDownLatchTest
  94. {
  95. public static void main(String[] args)
  96. {
  97. // TODO Auto-generated method stub
  98. new Driver().main();
  99. }
  100.  
  101. }

  

  1. 通过Executor启动线程:

    1. class CountDownLatchDriver2
    2. {
    3. private static final int TOTAL_THREADS = 10;
    4. private final CountDownLatch mDoneSignal = new CountDownLatch(TOTAL_THREADS);
    5.  
    6. void main()
    7. {
    8. System.out.println("Main Thread Now:" + System.currentTimeMillis());
    9. doPrepareWork();// 准备工作
    10.  
    11. Executor executor = Executors.newFixedThreadPool(TOTAL_THREADS);
    12. for (int i = 0; i < TOTAL_THREADS; i++)
    13. {
    14. // 通过内建的线程池维护创建的线程
    15. executor.execute(new RunnableWorker(mDoneSignal, i));
    16. }
    17. doSomethingElse();// 做点自己的事情
    18. try
    19. {
    20. mDoneSignal.await();// 等待所有工作线程结束
    21. }
    22. catch (InterruptedException e)
    23. {
    24. // TODO Auto-generated catch block
    25. e.printStackTrace();
    26. }
    27. System.out.println("All workers have finished now.");
    28. System.out.println("Main Thread Now:" + System.currentTimeMillis());
    29. }
    30.  
    31. void doPrepareWork()
    32. {
    33. System.out.println("Ready,GO!");
    34. }
    35.  
    36. void doSomethingElse()
    37. {
    38. for (int i = 0; i < 100000; i++)
    39. {
    40. ;// delay
    41. }
    42. System.out.println("Main Thread Do something else.");
    43. }
    44. }
    45.  
    46. class RunnableWorker implements Runnable
    47. {
    48.  
    49. private final CountDownLatch mDoneSignal;
    50. private final int mThreadIndex;
    51.  
    52. RunnableWorker(final CountDownLatch doneSignal, final int threadIndex)
    53. {
    54. this.mDoneSignal = doneSignal;
    55. this.mThreadIndex = threadIndex;
    56. }
    57.  
    58. @Override
    59. public void run()
    60. {
    61. // TODO Auto-generated method stub
    62.  
    63. doWork();// 具体操作
    64. System.out.println("Thread " + mThreadIndex + " Done Now:"
    65. + System.currentTimeMillis());
    66. mDoneSignal.countDown();// 完成以后计数减一
    67. // 计数为0时,主线程接触阻塞,继续执行其他任务
    68. try
    69. {
    70. // 可以继续做点其他的事情,与主线程无关了
    71. Thread.sleep(5000);
    72. System.out.println("Thread " + mThreadIndex
    73. + " Do something else after notifing main thread");
    74.  
    75. }
    76. catch (InterruptedException e)
    77. {
    78. // TODO Auto-generated catch block
    79. e.printStackTrace();
    80. }
    81.  
    82. }
    83.  
    84. public void doWork()
    85. {
    86. for (int i = 0; i < 1000000; i++)
    87. {
    88. ;// 耗时操作
    89. }
    90. System.out.println("Thread " + mThreadIndex + ":do work");
    91. }
    92. }

      

    1. class CountDownLatchDriver2
    2. {
    3. private static final int TOTAL_THREADS = 10;
    4. private final CountDownLatch mDoneSignal = new CountDownLatch(TOTAL_THREADS);
    5.  
    6. void main()
    7. {
    8. System.out.println("Main Thread Now:" + System.currentTimeMillis());
    9. doPrepareWork();// 准备工作
    10.  
    11. Executor executor = Executors.newFixedThreadPool(TOTAL_THREADS);
    12. for (int i = 0; i < TOTAL_THREADS; i++)
    13. {
    14. // 通过内建的线程池维护创建的线程
    15. executor.execute(new RunnableWorker(mDoneSignal, i));
    16. }
    17. doSomethingElse();// 做点自己的事情
    18. try
    19. {
    20. mDoneSignal.await();// 等待所有工作线程结束
    21. }
    22. catch (InterruptedException e)
    23. {
    24. // TODO Auto-generated catch block
    25. e.printStackTrace();
    26. }
    27. System.out.println("All workers have finished now.");
    28. System.out.println("Main Thread Now:" + System.currentTimeMillis());
    29. }
    30.  
    31. void doPrepareWork()
    32. {
    33. System.out.println("Ready,GO!");
    34. }
    35.  
    36. void doSomethingElse()
    37. {
    38. for (int i = 0; i < 100000; i++)
    39. {
    40. ;// delay
    41. }
    42. System.out.println("Main Thread Do something else.");
    43. }
    44. }
    45.  
    46. class RunnableWorker implements Runnable
    47. {
    48.  
    49. private final CountDownLatch mDoneSignal;
    50. private final int mThreadIndex;
    51.  
    52. RunnableWorker(final CountDownLatch doneSignal, final int threadIndex)
    53. {
    54. this.mDoneSignal = doneSignal;
    55. this.mThreadIndex = threadIndex;
    56. }
    57.  
    58. @Override
    59. public void run()
    60. {
    61. // TODO Auto-generated method stub
    62.  
    63. doWork();// 具体操作
    64. System.out.println("Thread " + mThreadIndex + " Done Now:"
    65. + System.currentTimeMillis());
    66. mDoneSignal.countDown();// 完成以后计数减一
    67. // 计数为0时,主线程接触阻塞,继续执行其他任务
    68. try
    69. {
    70. // 可以继续做点其他的事情,与主线程无关了
    71. Thread.sleep(5000);
    72. System.out.println("Thread " + mThreadIndex
    73. + " Do something else after notifing main thread");
    74.  
    75. }
    76. catch (InterruptedException e)
    77. {
    78. // TODO Auto-generated catch block
    79. e.printStackTrace();
    80. }
    81.  
    82. }
    83.  
    84. public void doWork()
    85. {
    86. for (int i = 0; i < 1000000; i++)
    87. {
    88. ;// 耗时操作
    89. }
    90. System.out.println("Thread " + mThreadIndex + ":do work");
    91. }
    92. }

      

    1. 输出:

      1. Main Thread Now:1359959480786
      2. Ready,GO!
      3. Thread 0:do work
      4. Thread 0 Done Now:1359959480808
      5. Thread 1:do work
      6. Thread 1 Done Now:1359959480811
      7. Thread 2:do work
      8. Thread 2 Done Now:1359959480813
      9. Main Thread Do something else.
      10. Thread 3:do work
      11. Thread 3 Done Now:1359959480825
      12. Thread 5:do work
      13. Thread 5 Done Now:1359959480827
      14. Thread 7:do work
      15. Thread 7 Done Now:1359959480829
      16. Thread 9:do work
      17. Thread 9 Done Now:1359959480831
      18. Thread 4:do work
      19. Thread 4 Done Now:1359959480833
      20. Thread 6:do work
      21. Thread 6 Done Now:1359959480835
      22. Thread 8:do work
      23. Thread 8 Done Now:1359959480837
      24. All workers have finished now.
      25. Main Thread Now:1359959480838
      26. Thread 0 Do something else after notifing main thread
      27. Thread 1 Do something else after notifing main thread
      28. Thread 2 Do something else after notifing main thread
      29. Thread 3 Do something else after notifing main thread
      30. Thread 9 Do something else after notifing main thread
      31. Thread 7 Do something else after notifing main thread
      32. Thread 5 Do something else after notifing main thread
      33. Thread 4 Do something else after notifing main thread
      34. Thread 6 Do something else after notifing main thread
      35. Thread 8 Do something else after notifing main thread

        

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

例1

  1. 例2

      1. class WalkTarget
      2. {
      3. private final int mCount = 5;
      4. private final CyclicBarrier mBarrier;
      5. ExecutorService mExecutor;
      6.  
      7. class BarrierAction implements Runnable
      8. {
      9. @Override
      10. public void run()
      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, new BarrierAction());
      23. mExecutor = Executors.newFixedThreadPool(mCount);
      24.  
      25. for (int i = 0; i < mCount; i++)
      26. {
      27. //启动工作线程
      28. mExecutor.execute(new Walker(mBarrier, i));
      29. }
      30. }
      31. }
      32.  
      33. //工作线程
      34. class Walker implements Runnable
      35. {
      36. private final CyclicBarrier mBarrier;
      37. private final int mThreadIndex;
      38.  
      39. Walker(final CyclicBarrier barrier, final int threadIndex)
      40.  
      41. {
      42. mBarrier = barrier;
      43. mThreadIndex = threadIndex;
      44. }
      45.  
      46. @Override
      47. public void run()
      48. {
      49. // TODO Auto-generated method stub
      50. System.out.println("Thread " + mThreadIndex + " is running...");
      51. // 执行任务
      52. try
      53. {
      54. TimeUnit.MILLISECONDS.sleep(5000);
      55. // do task
      56. }
      57. catch (InterruptedException e)
      58. {
      59. // TODO Auto-generated catch block
      60. e.printStackTrace();
      61. }
      62.  
      63. // 完成任务以后,等待其他线程完成任务
      64. try
      65. {
      66. mBarrier.await();
      67. }
      68. catch (InterruptedException e)
      69. {
      70. // TODO Auto-generated catch block
      71. e.printStackTrace();
      72. }
      73. catch (BrokenBarrierException e)
      74. {
      75. // TODO Auto-generated catch block
      76. e.printStackTrace();
      77. }
      78. // 其他线程任务都完成以后,阻塞解除,可以继续接下来的任务
      79. System.out.println("Thread " + mThreadIndex + " do something else");
      80. }
      81.  
      82. }
      83.  
      84. public class CountDownLatchTest
      85. {
      86. public static void main(String[] args)
      87. {
      88. // TODO Auto-generated method stub
      89. //new CountDownLatchDriver2().main();
      90. new WalkTarget();
      91. }
      92.  
      93. }

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

      1. 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简单比较:

CountDownLatch

CyclicBarrier

软件包

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

}

 

java中等待所有线程都执行结束的更多相关文章

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

    转自:http://blog.csdn.net/liweisnake/article/details/12966761 今天看到一篇文章,是关于java中如何等待所有线程都执行结束,文章总结得很好,原 ...

  2. JAVA中等待所有线程都执行结束(转2)

    场景: package com.java4all.mypoint; import java.util.concurrent.CountDownLatch; public class ThreadTes ...

  3. Java中如何保证线程顺序执行

    只要了解过多线程,我们就知道线程开始的顺序跟执行的顺序是不一样的.如果只是创建三个线程然后执行,最后的执行顺序是不可预期的.这是因为在创建完线程之后,线程执行的开始时间取决于CPU何时分配时间片,线程 ...

  4. 【java】【多线程】等待开启的多个线程都执行完成,再做事情,怎么实现

    今天在controller中写一个接口用来测试模拟多个请求同时到达 下订单的情况, 怎么能有效保证高并发下的库存和销量的一致性呢?[具体实现方法:https://www.cnblogs.com/sxd ...

  5. Java中的守护线程 & 非守护线程(简介)

    Java中的守护线程 & 非守护线程 守护线程 (Daemon Thread) 非守护线程,又称用户线程(User Thread) 用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守 ...

  6. Java 中如何实现线程间通信

    世界以痛吻我,要我报之以歌 -- 泰戈尔<飞鸟集> 虽然通常每个子线程只需要完成自己的任务,但是有时我们希望多个线程一起工作来完成一个任务,这就涉及到线程间通信. 关于线程间通信本文涉及到 ...

  7. Java中的守护线程和非守护线程(转载)

    <什么是守护线程,什么是非守护线程> Java有两种Thread:"守护线程Daemon"(守护线程)与"用户线程User"(非守护线程). 用户线 ...

  8. 详解线程池的作用及Java中如何使用线程池

    服务端应用程序(如数据库和 Web 服务器)需要处理来自客户端的高并发.耗时较短的请求任务,所以频繁的创建处理这些请求的所需要的线程就是一个非常消耗资源的操作.常规的方法是针对一个新的请求创建一个新线 ...

  9. 重新想象 Windows 8 Store Apps (42) - 多线程之线程池: 延迟执行, 周期执行, 在线程池中找一个线程去执行指定的方法

    [源码下载] 重新想象 Windows 8 Store Apps (42) - 多线程之线程池: 延迟执行, 周期执行, 在线程池中找一个线程去执行指定的方法 作者:webabcd 介绍重新想象 Wi ...

随机推荐

  1. 利用jenkins和docker实现持续交付

    利用jenkins和docker实现持续交付 一.什么是持续交付 让软件产品的产出过程在一个短周期内完成,以保证软件可以稳定.持续的保持在随时可以发布的状况.它的目标在于让软件的构建.测试与发布变得更 ...

  2. 洛谷 - P2730 - 魔板 Magic Squares - bfs

    写状态转移弄了很久,老了,不记得自己的数组是怎么标号的了. #include <bits/stdc++.h> using namespace std; #define ll long lo ...

  3. MongoDb 本机删除密码的方法

    Terminal Inflection LINUX ESOTERICA, FIXES AND RANTS About Errors Resolved Linux Recommended Books W ...

  4. JDK由浅到深的理解

    一.JDK的安装 二.JDK安装后目录下的文件夹的用处 bin:编译器(javac.exe); db:jdk从1.6之后内置Derby数据库,它是一个纯用Java实现的内存数据库,Apache的开源项 ...

  5. 黑客攻防技术宝典web实战篇:工具web服务器习题

    猫宁!!! 参考链接:http://www.ituring.com.cn/book/885 随书答案. 1. 在什么情况下 Web 服务器会显示目录列表? 如果请求某目录的 URL 且满足以下条件,W ...

  6. Swift typealias associatedType

    使用typealias为常用数据类型起一个别名, 一方面更容易通过别名理解该类型的用途, 另一方面还可以减少日常开发的代码量. typealias使用实例: // 网络请求常用回调闭包 typeali ...

  7. gvim 常用键

    按i 切换到插入模式,ESC退出 命令模式下 dd 删除一行 dw 删除一个字 x 删除一个字符 :set num  设置行号 :reg 查看寄存器 p 粘贴 y 复制 "*p  不同环境下 ...

  8. Vue-cli构建项目, 组件中js代码引入图片路径问题

    问题描述 .vue的组件分成三个部分, template结构部分, script路径代码, style页面样式 首先, 我们可以在template可以正确引入, 无论是dev, 还是build都没有问 ...

  9. Android应用的安全隐患*

    转自: http://www.cnblogs.com/chi0591/p/3864747.html Android应用的安全隐患包括三个方面: 代码安全 数据安全 组件安全 代码安全 代码安全主要是指 ...

  10. Android中ProgressBar显示小数的方法

    Android原生的ProgressBar的ProgressDialog.STYLE_HORIZONTAL(即水平样式)默认setMax和setProgress只能传int型的参数,而实际项目中我需要 ...