Java:多執行緒等待所有執行緒結束(CountDownLatch/CyclicBarrier) .
本文主要是參考官方文件做一學習用途。
官方連結:
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/CountDownLatch.html
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/CyclicBarrier.html
多執行緒設計過程中,經常會遇到需要等待其它執行緒結束以後再做其他事情的情況,比如多執行緒下載檔案,每個執行緒都會下載檔案的一部分,在所有執行緒結束以後,需要將各部分再次拼接成一個完整的檔案。
有幾種方案:
1.在主執行緒中設定一自定義全域性計數標誌,在工作執行緒完成時,計數減一。主執行緒偵測該標誌是否為0,一旦為0,表示所有工作執行緒已經完成。
2.使用Java標準的類CountDownLatch來完成這項工作,原理是一樣的,計數。
CountDownLatch
CountDownLatch 初始化設定count,即等待(await)count個執行緒或一個執行緒count次計數,通過工作執行緒來countDown計數減一,直到計數為0,await阻塞結束。
設定的count不可更改,如需要動態設定計數的執行緒數,可以使用CyclicBarrier.
下面的例子,所有的工作執行緒中準備就緒以後,並不是直接執行,而是等待主執行緒的訊號後再執行具體的操作。
- package com.example.multithread;
- import java.util.concurrent.CountDownLatch;
- class Driver
- {
- private static final int TOTAL_THREADS = 10;
- private final CountDownLatch mStartSignal = new CountDownLatch(1);
- private final CountDownLatch mDoneSignal = new CountDownLatch(TOTAL_THREADS);
- void main()
- {
- for (int i = 0; i < TOTAL_THREADS; i++)
- {
- new Thread(new Worker(mStartSignal, mDoneSignal, i)).start();
- }
- System.out.println("Main Thread Now:" + System.currentTimeMillis());
- doPrepareWork();// 準備工作
- mStartSignal.countDown();// 計數減一為0,工作執行緒真正啟動具體操作
- doSomethingElse();//做點自己的事情
- try
- {
- mDoneSignal.await();// 等待所有工作執行緒結束
- }
- catch (InterruptedException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- System.out.println("All workers have finished now.");
- System.out.println("Main Thread Now:" + System.currentTimeMillis());
- }
- void doPrepareWork()
- {
- System.out.println("Ready,GO!");
- }
- void doSomethingElse()
- {
- for (int i = 0; i < 100000; i++)
- {
- ;// delay
- }
- System.out.println("Main Thread Do something else.");
- }
- }
- class Worker implements Runnable
- {
- private final CountDownLatch mStartSignal;
- private final CountDownLatch mDoneSignal;
- private final int mThreadIndex;
- Worker(final CountDownLatch startSignal, final CountDownLatch doneSignal,
- final int threadIndex)
- {
- this.mDoneSignal = doneSignal;
- this.mStartSignal = startSignal;
- this.mThreadIndex = threadIndex;
- }
- @Override
- public void run()
- {
- // TODO Auto-generated method stub
- try
- {
- mStartSignal.await();// 阻塞,等待mStartSignal計數為0執行後面的程式碼
- // 所有的工作執行緒都在等待同一個啟動的命令
- doWork();// 具體操作
- System.out.println("Thread " + mThreadIndex + " Done Now:"
- + System.currentTimeMillis());
- mDoneSignal.countDown();// 完成以後計數減一
- }
- catch (InterruptedException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- public void doWork()
- {
- for (int i = 0; i < 1000000; i++)
- {
- ;// 耗時操作
- }
- System.out.println("Thread " + mThreadIndex + ":do work");
- }
- }
- public class CountDownLatchTest
- {
- public static void main(String[] args)
- {
- // TODO Auto-generated method stub
- new Driver().main();
- }
- }
通過Executor啟動執行緒:
- class CountDownLatchDriver2
- {
- private static final int TOTAL_THREADS = 10;
- private final CountDownLatch mDoneSignal = new CountDownLatch(TOTAL_THREADS);
- void main()
- {
- System.out.println("Main Thread Now:" + System.currentTimeMillis());
- doPrepareWork();// 準備工作
- Executor executor = Executors.newFixedThreadPool(TOTAL_THREADS);
- for (int i = 0; i < TOTAL_THREADS; i++)
- {
- // 通過內建的執行緒池維護建立的執行緒
- executor.execute(new RunnableWorker(mDoneSignal, i));
- }
- doSomethingElse();// 做點自己的事情
- try
- {
- mDoneSignal.await();// 等待所有工作執行緒結束
- }
- catch (InterruptedException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- System.out.println("All workers have finished now.");
- System.out.println("Main Thread Now:" + System.currentTimeMillis());
- }
- void doPrepareWork()
- {
- System.out.println("Ready,GO!");
- }
- void doSomethingElse()
- {
- for (int i = 0; i < 100000; i++)
- {
- ;// delay
- }
- System.out.println("Main Thread Do something else.");
- }
- }
- class RunnableWorker implements Runnable
- {
- private final CountDownLatch mDoneSignal;
- private final int mThreadIndex;
- RunnableWorker(final CountDownLatch doneSignal, final int threadIndex)
- {
- this.mDoneSignal = doneSignal;
- this.mThreadIndex = threadIndex;
- }
- @Override
- public void run()
- {
- // TODO Auto-generated method stub
- doWork();// 具體操作
- System.out.println("Thread " + mThreadIndex + " Done Now:"
- + System.currentTimeMillis());
- mDoneSignal.countDown();// 完成以後計數減一
- // 計數為0時,主執行緒接觸阻塞,繼續執行其他任務
- try
- {
- // 可以繼續做點其他的事情,與主執行緒無關了
- Thread.sleep(5000);
- System.out.println("Thread " + mThreadIndex
- + " Do something else after notifing main thread");
- }
- catch (InterruptedException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- public void doWork()
- {
- for (int i = 0; i < 1000000; i++)
- {
- ;// 耗時操作
- }
- System.out.println("Thread " + mThreadIndex + ":do work");
- }
- }
輸出:
Main Thread Now:1359959480786 Ready,GO! Thread 0:do work Thread 0 Done Now:1359959480808 Thread 1:do work Thread 1 Done Now:1359959480811 Thread 2:do work Thread 2 Done Now:1359959480813 Main Thread Do something else. Thread 3:do work Thread 3 Done Now:1359959480825 Thread 5:do work Thread 5 Done Now:1359959480827 Thread 7:do work Thread 7 Done Now:1359959480829 Thread 9:do work Thread 9 Done Now:1359959480831 Thread 4:do work Thread 4 Done Now:1359959480833 Thread 6:do work Thread 6 Done Now:1359959480835 Thread 8:do work Thread 8 Done Now:1359959480837 All workers have finished now. Main Thread Now:1359959480838 Thread 0 Do something else after notifing main thread Thread 1 Do something else after notifing main thread Thread 2 Do something else after notifing main thread Thread 3 Do something else after notifing main thread Thread 9 Do something else after notifing main thread Thread 7 Do something else after notifing main thread Thread 5 Do something else after notifing main thread Thread 4 Do something else after notifing main thread Thread 6 Do something else after notifing main thread Thread 8 Do something else after notifing main thread
CyclicBarrier
使用CyclickBarrier的例子:
- class WalkTarget
- {
- private final int mCount = 5;
- private final CyclicBarrier mBarrier;
- ExecutorService mExecutor;
- class BarrierAction implements Runnable
- {
- @Override
- public void run()
- {
- // TODO Auto-generated method stub
- System.out.println("所有執行緒都已經完成任務,計數達到預設值");
- //mBarrier.reset();//恢復到初始化狀態
- }
- }
- WalkTarget()
- {
- //初始化CyclicBarrier
- mBarrier = new CyclicBarrier(mCount, new BarrierAction());
- mExecutor = Executors.newFixedThreadPool(mCount);
- for (int i = 0; i < mCount; i++)
- {
- //啟動工作執行緒
- mExecutor.execute(new Walker(mBarrier, i));
- }
- }
- }
- //工作執行緒
- class Walker implements Runnable
- {
- private final CyclicBarrier mBarrier;
- private final int mThreadIndex;
- Walker(final CyclicBarrier barrier, final int threadIndex)
- {
- mBarrier = barrier;
- mThreadIndex = threadIndex;
- }
- @Override
- public void run()
- {
- // TODO Auto-generated method stub
- System.out.println("Thread " + mThreadIndex + " is running...");
- // 執行任務
- try
- {
- TimeUnit.MILLISECONDS.sleep(5000);
- // do task
- }
- catch (InterruptedException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- // 完成任務以後,等待其他執行緒完成任務
- try
- {
- mBarrier.await();
- }
- catch (InterruptedException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- catch (BrokenBarrierException e)
- {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- // 其他執行緒任務都完成以後,阻塞解除,可以繼續接下來的任務
- System.out.println("Thread " + mThreadIndex + " do something else");
- }
- }
- public class CountDownLatchTest
- {
- public static void main(String[] args)
- {
- // TODO Auto-generated method stub
- //new CountDownLatchDriver2().main();
- new WalkTarget();
- }
- }
輸出(注意,只有所有的執行緒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
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 多執行緒CountDownLatchJava執行緒CountDownLatch
- java多執行緒系列:CountDownLatchJava執行緒CountDownLatch
- Java CompletableFuture:allOf等待所有非同步執行緒任務結束Java非同步執行緒
- Win32執行緒——等待另一個執行緒結束Win32執行緒
- java多執行緒10:併發工具類CountDownLatch、CyclicBarrier和SemaphoreJava執行緒CountDownLatch
- Java 多執行緒基礎 - CyclicBarrierJava執行緒
- 併發工具類(一)等待多執行緒的CountDownLatch執行緒CountDownLatch
- Java 多執行緒基礎(六)執行緒等待與喚醒Java執行緒
- Windows10 VS2017 C++多執行緒傳參和等待執行緒結束WindowsC++執行緒
- java-多執行緒-CountDownLatch(閉鎖) CyclicBarrier(柵欄) Semaphore(訊號量)-Java執行緒CountDownLatch
- Java多執行緒同步工具類之CyclicBarrierJava執行緒
- Java多執行緒-執行緒中止Java執行緒
- Java多執行緒同步工具類之CountDownLatchJava執行緒CountDownLatch
- Java多種方法實現等待所有子執行緒完成再繼續執行Java執行緒
- Java多執行緒之執行緒中止Java執行緒
- Java多執行緒-執行緒狀態Java執行緒
- Java多執行緒-執行緒通訊Java執行緒
- java 多執行緒守護執行緒Java執行緒
- Java多執行緒(2)執行緒鎖Java執行緒
- java多執行緒9:執行緒池Java執行緒
- 【java多執行緒】(二)執行緒停止Java執行緒
- 【多執行緒總結(二)-執行緒安全與執行緒同步】執行緒
- 【Java多執行緒】執行緒安全的集合Java執行緒
- 【Java】【多執行緒】執行緒池簡述Java執行緒
- Java多執行緒-執行緒池的使用Java執行緒
- 【Java多執行緒】輕鬆搞定Java多執行緒(二)Java執行緒
- Java多執行緒(一)多執行緒入門篇Java執行緒
- java——多執行緒Java執行緒
- java多執行緒Java執行緒
- Java - 多執行緒Java執行緒
- java 多執行緒Java執行緒
- 多執行緒--執行緒管理執行緒
- 執行緒與多執行緒執行緒
- 多執行緒【執行緒池】執行緒
- 多執行緒------執行緒與程式/執行緒排程/建立執行緒執行緒
- Java多執行緒學習(一)Java多執行緒入門Java執行緒
- 最全java多執行緒總結2--如何進行執行緒同步Java執行緒
- Java多執行緒學習——執行緒通訊Java執行緒
- Java多執行緒學習(2)執行緒控制Java執行緒