參考:
https://www.jianshu.com/p/1716ce690637
http://ifeve.com/talk-concurrency-countdownlatch/
CountDownLatch是什麼
CountDownLatch也叫閉鎖,在JDK1.5被引入,允許一個或多個線程等待其他線程完成操作後再執行。
CountDownLatch內部會維護一個初始值為線程數量的計數器,主線程執行await方法,如果計數器大於0,則阻塞等待。當一個線程完成任務後,計數器值減1。當計數器為0時,表示所有的線程已經完成任務,等待的主線程被喚醒繼續執行。 應用情境
應用程式的主線程希望在負責啟動架構服務的線程已經完成之後再執行。在這個需求中,要實現主線程等待所有線程完成的操作,最簡單的做法是使用join。但在JDK1.5之後的並發包中提供的CountDownLatch也可以實現join的這個功能,並且比join的功能更多。
例子1:
public class StartUpCheckService {private SimpleDateFormat sdf = new SimpleDateFormat("[YYYY-MM-dd HH:mm:ss:SSS] ");private void println(String msg) {System.out.println(sdf.format(new Date()) + msg);}/** * 所有服務的基類,具體實現在execute方法實現 */class Service implements Runnable { private CountDownLatch latch; public Service(CountDownLatch latch) { this.latch = latch; } @Override public void run() { try { execute(); } finally { if (latch != null) latch.countDown(); } } public void execute() {}}/** * 服務具體實作類別.工作2秒 */class HealthCheckService extends Service { public HealthCheckService(CountDownLatch latch) { super(latch); } @Override public void execute() { try { println("HealthCheckService work..."); TimeUnit.SECONDS.sleep(2); println("HealthCheckService done..."); } catch (InterruptedException e) { e.printStackTrace(); } }}/** * 服務具體實作類別.工作3秒 */class DatabaseCheckService extends Service { public DatabaseCheckService(CountDownLatch latch) { super(latch); } @Override public void execute() { try { println("DatabaseCheckService work..."); TimeUnit.SECONDS.sleep(3); println("DatabaseCheckService done..."); } catch (InterruptedException e) { e.printStackTrace(); } }}/** * 應用啟動類,使用線程池執行每個服務的任務。負責初始化閉鎖,然後等待,直到所有服務都被檢測完。 */class Application { private CountDownLatch latch; public void startUp() throws Exception { latch = new CountDownLatch(2); List<Service> services = new ArrayList<>(); services.add(new DatabaseCheckService(latch)); services.add(new HealthCheckService(latch)); ExecutorService executor = Executors.newFixedThreadPool(services.size()); for (Service service : services) { executor.execute(service); } latch.await(); println("all service is start up"); executor.shutdown(); }}public static void main(String arg[]) {try {new StartUpCheckService().new Application().startUp();;} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}}
輸出:
例子2:
public class Test {public static void main(String[] args) {final CountDownLatch latch = new CountDownLatch(2);new Thread() {public void run() {try {System.out.println("子線程" + Thread.currentThread().getName() + "正在執行");Thread.sleep(3000);System.out.println("子線程" + Thread.currentThread().getName() + "執行完畢");latch.countDown();} catch (InterruptedException e) {e.printStackTrace();}};}.start();new Thread() {public void run() {try {System.out.println("子線程" + Thread.currentThread().getName() + "正在執行");Thread.sleep(3000);System.out.println("子線程" + Thread.currentThread().getName() + "執行完畢");latch.countDown();} catch (InterruptedException e) {e.printStackTrace();}};}.start();try {System.out.println("等待2個子線程執行完畢...");latch.await();System.out.println("2個子線程已經執行完畢");System.out.println("繼續執行主線程");} catch (InterruptedException e) {e.printStackTrace();}}}
輸出:
子線程Thread-0正在執行等待2個子線程執行完畢...子線程Thread-1正在執行子線程Thread-1執行完畢子線程Thread-0執行完畢2個子線程已經執行完畢繼續執行主線程
CyclicBarrier和CountDownLatch的區別 CountDownLatch的計數器只能使用一次。而CyclicBarrier的計數器可以使用reset() 方法重設。所以CyclicBarrier能處理更為複雜的業務情境,比如如果計算髮生錯誤,可以重設計數器,並讓線程們重新執行一次。 CyclicBarrier還提供其他有用的方法,比如getNumberWaiting方法可以獲得CyclicBarrier阻塞的線程數量。isBroken方法用來知道阻塞的線程是否被中斷。 CountDownLatch和CyclicBarrier都能夠實現線程之間的等待,只不過它們側重點不同:CountDownLatch一般用於某個線程A等待若干個其他線程執行完任務之後,它才執行;而CyclicBarrier一般用於一組線程互相等待至某個狀態,然後這一組線程再同時執行;