在前面的Java學習筆記中,總結了Java中進行多線程同步的幾個方法:java
一、synchronized關鍵字進行同步。編程
二、Lock鎖接口及其實現類ReentrantLock、ReadWriteLock鎖實現同步。網絡
三、信號量Semaphore實現同步。多線程
其中,synchronized關鍵字和Lock鎖解決的是多個線程對同一資源的併發訪問問題。信號量Semaphore解決的是多副本資源的共享訪問問題。併發
今天,來學習一下Java中的另一個多線程同步輔助類:CountDownLatch。官方文檔對CountDownLatch的解釋是:在完成一組正在其餘線程中執行的操做以前,它容許一個或多個線程一直等待。也就是說,CountDownLatch控制某個或者多個線程,讓它們等待多個線程完成某項任務後,再啓動。CountDownLatch主要是用來同步多個任務的執行,區別於其餘的synchronized關鍵字,鎖,信號量是用來同步共享資源的。ide
CountDownLatch內部維護一個計數器,計數器的值爲待完成的任務數N,須要等待這N個任務完成的線程調用CountDownLatch的await()方法使本身進入休眠等待狀態。函數
當某一個任務線程完成某一個任務後調用CountDownLatch的countDown()方法來表示本身的任務已完成,此時CountDownLatch的計數器值減1,當全部的任務完成式,計數器的值爲0。當計數器值爲0時,CountDownLatch將喚醒全部因await()方法進入休眠的線程。學習
CountDownLatch的使用主要有3點:this
一、CountDownLatch的聲明及初始化,在初始化時須要指定等待完成的任務數。spa
二、某一個任務完成時調用CountDownLatch的countDown()方法,向CountDownLatch報告本身的任務已經完成,
三、須要等待任務完成的線程調用CountDownLatch的await()方法,調用後該線程將進入休眠,並在全部任務數完成後CountDownLatch的計數器值爲0時,因await()方法進行休眠的線程將被喚醒。
在此本人在Java 7併發編程實戰手冊該書中的CountDownLatch使用示例的基礎上作了部分改進,來演示CountDownLatch的使用詳情:
模擬10個參會者和一個主持人蔘加的一個會以,每一個參會者及主持人須要等待其餘的參會者均到場簽到以後,才能開始會以併發言。爲此,先建立一個會以管理的類VideoConference,其提供一個arrive()方法供參會者調用來進行簽到。會議管理的擁有者是主持人,其等待每一個參會者的簽到:
public class VideoConference implements Runnable{ private final CountDownLatch countDownLatch; private int number; public VideoConference(int number) { this.number = number; this.countDownLatch = new CountDownLatch(number);//使用Number初始化其內部的計數器,當初始化完成後,不能再次初始化 } public void arrive(String name){ //每一個須要同步的任務,在任務完成時,須要調用該方法 countDownLatch.countDown();//countDownLatch內部的計數器減1 System.out.print("arrive:"+name+"\n"); try{ countDownLatch.await();//await方法是線程進入休眠,當countDownLatch計數器爲0時,將被喚醒 //線程被喚醒,在這裏能夠執行一系列任務 System.out.print("name:"+name + " say:let's start..." +"\n"); }catch (InterruptedException e){ e.printStackTrace(); } } public void run(){ System.out.print("has arrive:"+(number-countDownLatch.getCount())+"\n"); try{ countDownLatch.await();//await方法是線程進入休眠,當countDownLatch計數器爲0時,將被喚醒 //線程被喚醒,在這裏能夠執行一系列任務 System.out.print("all arrived:"+(number-countDownLatch.getCount())+"\n"); }catch (InterruptedException e){ e.printStackTrace(); } } }建立一個參會者類Participant:
public class Participant implements Runnable{ private VideoConference videoConference; private String name; public Participant(String name, VideoConference videoConference) { this.name = name; this.videoConference = videoConference; } public void run(){ try { //do something Thread.sleep(50); // videoConference.arrive(name); }catch (InterruptedException e){ e.printStackTrace(); } } public static void main(String[] args){ VideoConference videoConference = new VideoConference(10); Thread videoThread = new Thread(videoConference); videoThread.start(); for(int i=0; i<10; i++){ Thread thread = new Thread(new Participant("participant:"+i,videoConference)); thread.start(); } } }Participant類中的main函數首先建立了一個須要10個參會者參加的一個會議,以後,建立了10個參會者並逐個簽到,在10個參會者都簽到以後,每一個參會者及主持人將被"喚醒"併發言。
CountDownLatch類解決的是多線程間的同步等待、任務協調問題,應用在如在啓動某個程序的主功能前,須要前置完成配置環境檢查、網絡檢查等多個子任務等相似的場景。在Java中,除了使用CountDownLatch來實現多線程間的同步等待之外,還可使用柵欄技術CyclicBarrier來實現多線程間的同步等待、任務協調。