1. 程式人生 > 實用技巧 >常用輔助類(ContDownLatch、CyclicBarrier、Semaphore)

常用輔助類(ContDownLatch、CyclicBarrier、Semaphore)

1.ContDownLatch(倒計時器)

ContDownLatch是一個同步輔助類,在完成某些運算時,只有其他所有執行緒的運算全部完成,當前運算才繼續執行,這就叫閉鎖。

● CountDownLatch(int count):例項化一個倒計數器,count指定計數個數

● countDown():計數減一

● await():等待,當計數減到0時,所有執行緒並行執行

程式碼演示:火箭發射

public class ConutDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {

        
// 倒計時器:計數數量為10 CountDownLatch latch = new CountDownLatch(10); // 倒計時檢查任務,使用10個執行緒來完成任務 CountRunnable runnable = new CountRunnable(latch); for (int i = 0; i < 10; i++) { new Thread(runnable).start(); // 執行任務 } // 等待檢查完成 latch.await();
// 發射火箭 System.out.println("Fire!"); } } class CountRunnable implements Runnable { private CountDownLatch countDownLatch; public CountRunnable(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { // 模擬檢查任務
try { Thread.sleep(new Random().nextInt(10) * 1000); System.out.println("check complete"); } catch (InterruptedException e) { e.printStackTrace(); } finally { // 計數 -1 // 放在finally避免任務執行過程出現異常,導致countDown()不能被執行 countDownLatch.countDown(); } } }

上述程式碼中我們先生成了一個CountDownLatch例項。計數數量為10,這表示需要有10個執行緒來完成任務,等待在CountDownLatch上的執行緒才能繼續執行。latch.countDown();方法作用是通知CountDownLatch有一個執行緒已經準備完畢,倒計數器可以減一了。latch.await()方法要求主執行緒等待所有10個檢查任務全部準備好才一起並行執行。

2.CyclicBarrier(加法計數器)

一個同步輔助類,它允許一組執行緒互相等待,直到到達某個公共屏障點 (common barrier point)。在涉及一組固定大小的執行緒的程式中,這些執行緒必須不時地互相等待,此時 CyclicBarrier 很有用。因為該 barrier 在釋放等待執行緒後可以重用,所以稱它為迴圈 的 barrier。

使用場景

需要所有的子任務都完成時,才執行主任務,這個時候就可以選擇使用CyclicBarrier。

程式碼演示:集龍珠召喚神龍

public class CyclicBarrierDemo {
    public static void main(String[] args) {

        // 加法計數器,集齊7顆龍珠召喚神龍
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7, new Runnable() {
            @Override
            public void run() {
                System.out.println("集齊龍珠 -> 召喚神龍!嗷!嗷!嗷!!!");
            }
        });

        for (int i = 0; i < 7; i++) {
            new Thread(() -> {
                System.out.println("獲得(" + Thread.currentThread().getName() + ")星龍珠");
                try {
                    cyclicBarrier.await(); // 等待其它執行緒執行完成
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            }, String.valueOf(i + 1)).start();
        }
    }
}

◇ CountDownLatch 和 CyclicBarrier 比較

① CountDownLatch 是執行緒組之間的等待,即一個(或多個)執行緒等待N個執行緒完成某件事情之後再執行。

  CyclicBarrier 則是執行緒組內的等待,即每個執行緒相互等待,即 N 個執行緒都被攔截之後,然後依次執行。

② CountDownLatch 是減計數方式。

 CyclicBarrier 是加計數方式。

③ CountDownLatch 計數為 0 無法重置。

 CyclicBarrier 計數達到初始值,則可以重置。

④ CountDownLatch 不可以複用,

 CyclicBarrier 可以複用。

3.Semaphore(訊號量)

Semaphore 是一個計數訊號量,必須由獲取它的執行緒釋放。

作用:多個共享資源互斥的使用!併發限流,限制可以訪問某些資源的執行緒數量。

Semaphore 只有3個操作:① 初始化 ② 增加 ③ 減少

常用方法

public void  acquire();// 獲取一個許可
public void  acquire(intpermits);// 獲取permits個許可
public void  release();// 釋放一個許可
public void  release(intpermits);// 釋放permits個許可

- acquire:用來獲取一個許可,若無許可能夠獲得,則會一直等待,直到獲得許可。

- release:用來釋放許可。注意,在釋放許可之前,必須先獲獲得許可。

這4個方法都會被阻塞,如果想立即得到執行結果,可以使用下面幾個方法:

// 嘗試獲取一個許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回false
public  boolean  tryAcquire();

// 嘗試獲取一個許可,若在指定的時間內獲取成功,則立即返回true,否則則立即返回false
public  boolean  tryAcquire(longtimeout, TimeUnit unit);

// 嘗試獲取permits個許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回false
public  boolean  tryAcquire(intpermits);

// 嘗試獲取permits個許可,若在指定的時間內獲取成功,則立即返回true,否則則立即返回false
public  boolean  tryAcquire(intpermits, longtimeout, TimeUnit unit);

程式碼演示:搶車位

現有6輛車,但是隻有3個停車位

public class SemaphoreDemo {
    public static void main(String[] args) {

        // 訊號量,只允許 3 個執行緒同時訪問
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i < 6; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 獲取許可,將訊號量 +1,如果訊號量已經滿了,等待被釋放為止!
                    System.out.println("Car" + Thread.currentThread().getName() + " >>> 搶到車位");
                    TimeUnit.SECONDS.sleep(new Random().nextInt(10));
                    System.out.println("離開車位 >>> Car" + Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // 釋放,將訊號量 -1,然後喚醒等待的執行緒!
                }
            }, String.valueOf(i)).start();
        }
    }
}

◇ 總結

CountDownLatch 是一個執行緒等待其他執行緒, CyclicBarrier 是多個執行緒互相等待。

CountDownLatch 的計數是減 1 直到 0,CyclicBarrier 是加 1,直到指定值。

CountDownLatch 是一次性的, CyclicBarrier 可以迴圈利用。

CyclicBarrier 可以在最後一個執行緒達到屏障之前,選擇先執行一個操作。

Semaphore ,需要拿到許可才能執行,並可以選擇公平和非公平模式。