1. 程式人生 > 其它 >CountDownLatch、CyclicBarrier和Semaphore

CountDownLatch、CyclicBarrier和Semaphore

1、CountDownLatch用法

類似於計數器,比如某個任務需要等待另外N個任務執行完後再繼續執行,就可以用CountDownLatch實現。

構造方法:

//count為計數器值
public
CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }

另外還有三個主要方法:

//呼叫該方法的執行緒會進入阻塞狀態,等到count值為0時才會繼續執行
public void
await() throws InterruptedException { sync.acquireSharedInterruptibly(1); } //作用同上一個方法,只不過加了超時時間,即使count不為0但超過timeout後也會繼續執行 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } //將計數器count值減一
public void countDown() { sync.releaseShared(1); }

Demo:比如五個工人幹活,工頭要等他們一起全部幹完活才結算工資

public class Demo {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(5);

        for (int i = 0; i < 5; i ++) {
            final int worker = i;
            
new Thread(() -> { System.out.println("工人" + worker + "開始工作"); try{ //睡眠模擬幹活 Thread.sleep(worker * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("工人" + worker + "完成工作"); countDownLatch.countDown(); }).start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("所有工作已完成,工頭結算工資"); } }

列印結果:

工人0開始工作
工人1開始工作
工人0完成工作
工人4開始工作
工人2開始工作
工人3開始工作
工人1完成工作
工人2完成工作
工人3完成工作
工人4完成工作
所有工作已完成,工頭結算工資
View Code

2、CyclicBarrier

它是針對在某一組任務內相互等待,直到這一組任務都執行到某一個設定的點(呼叫await方法)之後再繼續各自執行後續操作。CyclicBarrier是可重用的,CountDownLatch則不行。

構造方法:

//構造引數為每組任務的任務數
public CyclicBarrier(int parties) {
        this(parties, null);
}

//barrierAction:可以在該組任務到達指定點後由最晚到達的執行緒執行額外的操作
public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
}

關鍵方法:

//呼叫該方法後執行緒進入等待,直到該組所有執行緒都執行到這一句再開始後續操作
public int await() throws InterruptedException, BrokenBarrierException {
        try {
            return dowait(false, 0L);
        } catch (TimeoutException toe) {
            throw new Error(toe); // cannot happen
        }
}

//同上個方法,多加了一個超時時間,超過設定時間直接執行後續操作
public int await(long timeout, TimeUnit unit)
        throws InterruptedException,
               BrokenBarrierException,
               TimeoutException {
        return dowait(true, unit.toNanos(timeout));
    }

Demo:比如體育課老師讓學生跑步,全部一起跑完才可以各自自由活動

public class Demo {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {
            System.out.println(Thread.currentThread().getName() + "告訴老師,所有人已完成跑步");
        });

        for (int i = 0; i < 3; i ++) {
            final int stu = i;
            new Thread(() -> {
                System.out.println("學生" + stu + "開始跑步");
                try{
                    //睡眠模擬跑步
                    Thread.sleep(2000);
                    System.out.println("學生" + stu + "已到達終點");
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                System.out.println("所有人到達終點,學生" + stu + "自由活動");

            }, "學生" + stu).start();
        }
    }
}

列印結果:

學生0開始跑步
學生1開始跑步
學生2開始跑步
學生2已到達終點
學生1已到達終點
學生0已到達終點
學生0告訴老師,所有人已完成跑步
所有人到達終點,學生0自由活動
所有人到達終點,學生1自由活動
所有人到達終點,學生2自由活動
View Code

3、Semaphore

訊號量,可以控制同時執行的執行緒數量,類似於鎖。

構造方法:

//permits為許可個數,預設為非公平的
public Semaphore(int permits) {
        sync = new NonfairSync(permits);
}

//fair表示是否公平的,公平的就是FIFO
public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

幾個關鍵方法:

//獲取一個許可,獲取不到時會進入阻塞等待
public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
}

//獲取permits個許可
public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
} 

//釋放一個許可
public void release() {
        sync.releaseShared(1);
}

//釋放permits個許可
public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
}

Demo:銀行櫃檯每個服務視窗同一時間只能服務一個顧客,其他顧客只能等待

public class Demo {
    public static void main(String[] args) throws Exception {

        //三個服務視窗
        Semaphore semaphore = new Semaphore(3);

        //9個顧客辦理業務
        for (int i = 0; i < 9; i ++) {
            final int customer = i;
            new Thread(() -> {
                try{
                    semaphore.acquire();
                    System.out.println("顧客" + customer + "獲得許可,櫃檯開始服務");

                    //睡眠模擬櫃檯服務
                    Thread.sleep(2000);
                    semaphore.release();
                    System.out.println("顧客" + customer + "業務已辦理完成,釋放許可");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

列印結果:

顧客2獲得許可,櫃檯開始服務
顧客2業務已辦理完成,釋放許可
顧客0業務已辦理完成,釋放許可
顧客3獲得許可,櫃檯開始服務
顧客4業務已辦理完成,釋放許可
顧客1獲得許可,櫃檯開始服務
顧客5獲得許可,櫃檯開始服務
顧客1業務已辦理完成,釋放許可
顧客5業務已辦理完成,釋放許可
顧客6獲得許可,櫃檯開始服務
顧客7獲得許可,櫃檯開始服務
顧客3業務已辦理完成,釋放許可
顧客8獲得許可,櫃檯開始服務
顧客8業務已辦理完成,釋放許可
顧客6業務已辦理完成,釋放許可
顧客7業務已辦理完成,釋放許可
View Code