天天看點

CountDownLatch

Java并發工具類 - CountDownLatch

1、簡介

  CountDownLatch是Java1.5之後引入的Java并發工具類,放在java.util.concurrent包下面 ​​http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html​​ 官方API。

  CountDownLatch能夠使一個或多個線程等待其他線程完成各自的工作後再執行;CountDownLatch是JDK 5+裡面閉鎖的一個實作。

  閉鎖(Latch):一種同步方法,可以延遲線程的進度直到線程到達某個終點狀态。通俗的講就是,一個閉鎖相當于一扇大門,在大門打開之前所有線程都被阻斷,一旦大門打開所有線程都将通過,但是一旦大門打開,所有線程都通過了,那麼這個閉鎖的狀态就失效了,門的狀态也就不能變了,隻能是打開狀态。也就是說閉鎖的狀态是一次性的,它確定在閉鎖打開之前所有特定的活動都需要在閉鎖打開之後才能完成。

  與CountDownLatch第一次互動是主線程等待其它的線程,主線程必須在啟動其它線程後立即調用await方法,這樣主線程的操作就會在這個方法上阻塞,直到其他線程完成各自的任務。

  其他的N個線程必須引用閉鎖對象,因為他們需要通知CountDownLatch對象,他們已經完成了各自的任務,這種機制就是通過countDown()方法來完成的。每調用一次這個方法,在構造函數中初始化的count值就減1,是以當N個線程都調用了這個方法count的值等于0,然後主線程就能通過await方法,恢複自己的任務。

  這裡的主線程是相對的概念,需要根據CountDownLatch建立的場景分析。

2、主要方法

特有方法: 
public CountDownLatch(int count); //指定計數的次數,隻能被設定1次
public void countDown();          //調用此方法則計數減1
public void await() throws InterruptedException   //調用此方法會一直阻塞目前線程,直到計時器的值為0,除非線程被中斷。
Public Long getCount();           //得到目前的計數
Public boolean await(long timeout, TimeUnit unit) //調用此方法會一直阻塞目前線程,直到計時器的值為0,除非線程被中斷或者計數器逾時,傳回false代表計數器逾時。
From Object Inherited:
Clone、equals、hashCode、notify、notifyALL、wait等。      

3、使用場景

(1)開啟多個線程分塊下載下傳一個大檔案,每個線程隻下載下傳固定的一截,最後由另外一個線程來拼接所有的分段。

(2)應用程式的主線程希望在負責啟動架構服務的線程已經啟動所有的架構服務之後再執行。

(3)確定一個計算不會執行,直到所需要的資源被初始化。

  ......

4、參考例子

(1)轉自:​​http://www.iteye.com/topic/1002652​​

CountDownLatch
public class CountDownLatchDemo {  
    final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    public static void main(String[] args) throws InterruptedException {  
        CountDownLatch latch=new CountDownLatch(2);//兩個勞工的協作  
        Worker worker1=new Worker("zhang san", 5000, latch);  
        Worker worker2=new Worker("li si", 8000, latch);  
        worker1.start();//  
        worker2.start();//  
        latch.await();//等待所有勞工完成工作  
        System.out.println("all work done at "+sdf.format(new Date()));  
    }  


    static class Worker extends Thread{  
        String workerName;   
        int workTime;  
        CountDownLatch latch;  
        public Worker(String workerName ,int workTime ,CountDownLatch latch){  
             this.workerName=workerName;  
             this.workTime=workTime;  
             this.latch=latch;  
        }  
        public void run(){  
            System.out.println("Worker "+workerName+" do work begin at "+sdf.format(new Date()));  
            doWork();//工作了  
            System.out.println("Worker "+workerName+" do work complete at "+sdf.format(new Date()));  
            latch.countDown();//勞工完成工作,計數器減一  

        }  

        private void doWork(){  
            try {  
                Thread.sleep(workTime);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
}      

(2)官網例子(推薦的實作模型1):​​http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html​​

class Driver { // ...
   void main() throws InterruptedException {
     CountDownLatch startSignal = new CountDownLatch(1);
     CountDownLatch doneSignal = new CountDownLatch(N);

     for (int i = 0; i < N; ++i) // create and start threads
       new Thread(new Worker(startSignal, doneSignal)).start();

     doSomethingElse();            // don't let run yet
     startSignal.countDown();      // let all threads proceed
     doSomethingElse();
     doneSignal.await();           // wait for all to finish
   }
 }

 class Worker implements Runnable {
   private final CountDownLatch startSignal;
   private final CountDownLatch doneSignal;
   Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {
      this.startSignal = startSignal;
      this.doneSignal = doneSignal;
   }
   public void run() {
      try {
        startSignal.await();
        doWork();
        doneSignal.countDown();
      } catch (InterruptedException ex) {} // return;
   }

   void doWork() { ... }
 }      

(3)官網例子(推薦的實作模型2):​​http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html​​

注:

  1. latch.countDown(); 建議放到finally語句裡。

  2. 對這個計數器的操作都是原子操作,同時隻能有一個線程去操作這個計數器。

5、常見面試題

  通過上述的介紹很容易解決下面的面試題:

  (1)解釋一下CountDownLatch概念?

  (2)CountDownLatch 和CyclicBarrier的不同之處?

  (3)給出一些CountDownLatch使用的例子?

  (4) CountDownLatch 類中主要的方法?

6、參考資料

     什麼時候使用CountDownLatch

  官網API:http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CountDownLatch.html