1. 程式人生 > >Java併發程式設計札記-(四)JUC鎖-08CountDownLatch

Java併發程式設計札記-(四)JUC鎖-08CountDownLatch

CountDownLatch是一個通用同步器,用於同步一個或多個任務。在完成一組正在其他執行緒中執行的任務之前,它允許一個或多個執行緒一直等待。

可以用一個初始計數值來初始化CountDownLatch物件,任何在這個物件上呼叫wait()的方法都將阻塞,直至計數值到達0。每完成一個任務,都可以在這個物件上呼叫countDown()減少計數值。當計數值減為0,所有等待的執行緒都會被釋放。CountDownLatch的計數值不能重置。如果需要重置計數器,請考慮使用CyclicBarrier。

使用場景

作為一個通用同步工具,CountDownLatch有許多用途。比如,將計數值初始化為1的CountDownLatch用作一個簡單的開/關:在通過呼叫countDown()的執行緒開啟入口前,所有呼叫await()的執行緒都一直在入口處等待;用N初始化的 CountDownLatch可以使一個執行緒在N個執行緒完成某項操作之前一直等待,或者使其在某項操作完成N次之前一直等待。

例1:CountDownLatch作為開關

public class Driver {
    public static void main(String args[]) throws InterruptedException {
        int n = 5;
        // 啟動訊號,在driver為繼續執行worker做好準備之前,它會阻止所有的worker繼續執行。
        CountDownLatch startSignal = new CountDownLatch(1);
        // 完成訊號,它允許driver在完成所有 worker之前一直等待。
CountDownLatch doneSignal = new CountDownLatch(n); for (int i = 0; i < n; ++i) // 建立並啟動所有執行緒 new Thread(new Worker(startSignal, doneSignal)).start(); Thread.sleep(1000); startSignal.countDown(); // 開啟startSignal開關,執行所有等待的任務 doneSignal.await();// 等待doneSignal計數器為0,即所有任務執行完
System.out.println("down"); } } 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 { System.out.println(Thread.currentThread().getName() + "啟動了"); startSignal.await();// 使當前執行緒在鎖存器startSignal倒計數至零之前一直等待 System.out.println(Thread.currentThread().getName() + "工作了"); doneSignal.countDown();// 遞減鎖存器doneSignal的計數 } catch (InterruptedException ex) { } } }

執行結果為:
Thread-2啟動了
Thread-0啟動了
Thread-1啟動了
Thread-3啟動了
Thread-4啟動了
Thread-2工作了
Thread-3工作了
Thread-1工作了
Thread-4工作了
Thread-0工作了
down

從結果中看到,5個任務執行緒依次啟動,但每一個都沒有執行完,這是因為run方法中的startSignal.await();使當前執行緒在鎖存器startSignal倒計數至零之前一直等待。直到startSignal.countDown(); 開啟startSignal開關,才執行所有等待的任務。

例2:CountDownLatch分割任務

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class Driver2 {
    public static void main(String args[]) throws InterruptedException {
        int n = 5;
        ExecutorService exec = Executors.newCachedThreadPool();
        // 完成訊號,它允許driver在完成所有 worker之前一直等待。
        CountDownLatch doneSignal = new CountDownLatch(n);
        // 建立並啟動所有執行緒
        for (int i = 0; i < n; ++i)
            exec.execute(new WorkerRunnable(doneSignal, i));
        // 等待doneSignal計數器為0,即所有任務執行完
        doneSignal.await();
        System.out.println("down");
        exec.shutdown();
    }
}

class WorkerRunnable implements Runnable {
    private final CountDownLatch doneSignal;
    private final int i;

    WorkerRunnable(CountDownLatch doneSignal, int i) {
        this.doneSignal = doneSignal;
        this.i = i;
    }

    public void run() {
        System.out.println("任務" + i + "完成了");
        doneSignal.countDown();// 遞減鎖存器doneSignal的計數
    }
}

原始碼

待補充

本文就講到這裡,想了解Java併發程式設計更多內容請參考: