天天看點

JAVA閉鎖CountDownLatch詳解閉鎖

閉鎖

閉鎖是一種同步工具類,可以延遲線程的進度直到其到達終止狀态。閉鎖的作用相當于一扇門:在閉鎖到達結束狀态之前,這扇門一直是關閉的,并且沒有任何線程能夠通過,當到達結束狀态時,這扇門會打來并允許所有的線程通過。當閉鎖到達結束狀态後,将不會再改變狀态,是以這扇門将永遠保持打開狀态。閉鎖可以用來確定某些活動直到其他活動都完成後才繼續執行,例如:

  • 確定某個計算在其需要的所有資源都被初始化之後才繼續執行。
  • 確定某個服務在其依賴的所有其他服務都已經啟動之後才啟動。
  • 等到直到直到某個操作的所有參與者(例如,在多玩家遊戲中的所有玩家)都就緒再繼續執行。

CountDownLatch是一種靈活的閉鎖實作。閉鎖狀态包括一個計數器,該計數器被初始化為一個正數,表示需要等待的事件數量。countDown方法遞減計數器,表示有一個事件已經發生了,而await方法等待計數器達到零,這表示所有需要等待的事件都已發生。如果計數器的值非零,那麼await方法會一直阻塞直到電腦為零,或者等待中的線程中斷,或者逾時。

下面是CountDownLatch的一個簡單例子:運作代碼看效果更容易了解

package com.joonwhee.imp;
 
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
 
/**
 * CountDownLatch的簡單例子
 * @author JoonWhee
 * @Date 2018年1月27日
 */
public class CountDownLatchTest {
    
    static CountDownLatch timeOutCountDownLatch = new CountDownLatch(1);
    
    public static void main(String args[]) {
        try {
            new Driver(10);
            testAwaitTimtOut();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
    // 測試帶逾時的await方法
    public static void testAwaitTimtOut() throws InterruptedException {
        System.out.println("before await(long timeout, TimeUnit unit)");
        timeOutCountDownLatch.await(3, TimeUnit.SECONDS);   //等待逾時時間為3秒
        System.out.println("after await(long timeout, TimeUnit unit)");
    }
 
}
 
class Driver { 
    public Driver(int N) throws InterruptedException {
        CountDownLatch startSignal = new CountDownLatch(1); // 定義一個CountDownLatch, 計數器值為1, 也就是每次await(), 需要執行1次countDown(), 才能繼續執行await()外面的代碼
        CountDownLatch doneSignal = new CountDownLatch(N);  // 定義一個CountDownLatch, 計數器值為N, 也就是每次await(), 需要執行N次countDown(), 才能繼續執行await()外面的代碼
 
        for (int i = 0; i < N; ++i) {
            // 建立并啟動線程
            new Thread(new Worker(startSignal, doneSignal)).start();
        }
        Thread.sleep(2000); // 睡眠2秒, 可以看到10個線程都在等待startSignal.countDown()執行
        System.out.println();
        startSignal.countDown(); // 解除所有線程的阻塞
        doneSignal.await(); // 等待所有線程執行doneSignal.countDown(), 才通過
        System.out.println();
        System.out.println("Main thread-after:doneSignal.await()------");
    }
}
 
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() + "-before:startSignal.await()");
            startSignal.await();    // 線程會在此處等待, 直到startSignal.countDown()執行
            System.out.println(Thread.currentThread().getName() + "-after:startSignal.await()");
            doneSignal.countDown();
        } catch (InterruptedException ex) {
        } // return;
    }
}
 
 
           

注意:

CountDownLatch同步機制Sync也是基于AQS的,但沒有公平和非公平模式之分;

/**
     * Synchronization control For CountDownLatch.
     * Uses AQS state to represent count.
     */
    private static final class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 4982264981922014374L;

        Sync(int count) {
            setState(count);
        }

        int getCount() {
            return getState();
        }

        protected int tryAcquireShared(int acquires) {
            return (getState() == 0) ? 1 : -1;
        }

        protected boolean tryReleaseShared(int releases) {
            // Decrement count; signal when transition to zero
            for (;;) {
                int c = getState();
                if (c == 0)
                    return false;
                int nextc = c-1;
                if (compareAndSetState(c, nextc))
                    return nextc == 0;
            }
        }
    }
           

常用方法

await()

/** Causes the current thread to wait until the latch has counted down to
     * zero, unless the thread is {@linkplain Thread#interrupt interrupted}.
     *
     * <p>If the current count is zero then this method returns immediately.
     *
     * <p>If the current count is greater than zero then the current
     * thread becomes disabled for thread scheduling purposes and lies
     * dormant until one of two things happen:
     * <ul>
     * <li>The count reaches zero due to invocations of the
     * {@link #countDown} method; or
     * <li>Some other thread {@linkplain Thread#interrupt interrupts}
     * the current thread.
     * </ul>
     **/

public void await() throws InterruptedException {}
           
countDown()      
/**
     * Decrements the count of the latch, releasing all waiting threads if
     * the count reaches zero.
     *
     * <p>If the current count is greater than zero then it is decremented.
     * If the new count is zero then all waiting threads are re-enabled for
     * thread scheduling purposes.
     *
     * <p>If the current count equals zero then nothing happens.
     */
    public void countDown() {}
           

繼續閱讀