1. 程式人生 > >Java中使用CountDownLatch進行多執行緒同步

Java中使用CountDownLatch進行多執行緒同步

CountDownLatch介紹

在前面的Java學習筆記中,總結了Java中進行多執行緒同步的幾個方法:

1、synchronized關鍵字進行同步。

2、Lock鎖介面及其實現類ReentrantLock、ReadWriteLock鎖實現同步。

3、訊號量Semaphore實現同步。

其中,synchronized關鍵字和Lock鎖解決的是多個執行緒對同一資源的併發訪問問題。訊號量Semaphore解決的是多副本資源的共享訪問問題。

今天,來學習一下Java中的另外一個多執行緒同步輔助類:CountDownLatch。官方文件對CountDownLatch的解釋是:在完成一組正在其他執行緒中執行的操作之前,它允許一個或多個執行緒一直等待。也就是說,CountDownLatch控制某個或者多個執行緒,讓它們等待多個執行緒完成某項任務後,再啟動。CountDownLatch主要是用來同步多個任務的執行,區別於其他的synchronized關鍵字,鎖,訊號量是用來同步共享資源的。

CountDownLatch實現原理簡介:

CountDownLatch內部維護一個計數器,計數器的值為待完成的任務數N,需要等待這N個任務完成的執行緒呼叫CountDownLatch的await()方法使自己進入休眠等待狀態。

當某一個任務執行緒完成某一個任務後呼叫CountDownLatch的countDown()方法來表示自己的任務已完成,此時CountDownLatch的計數器值減1,當所有的任務完成式,計數器的值為0。當計數器值為0時,CountDownLatch將喚醒所有因await()方法進入休眠的執行緒。

CountDownLatch的使用:

CountDownLatch的使用主要有3點:

1、CountDownLatch的宣告及初始化,在初始化時需要指定等待完成的任務數。

2、某一個任務完成時呼叫CountDownLatch的countDown()方法,向CountDownLatch報告自己的任務已經完成,

3、需要等待任務完成的執行緒呼叫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來實現多執行緒間的同步等待、任務協調。