1. 程式人生 > 其它 >JUC之ContDownLatch CyclicBarrier Semaphore使用記錄

JUC之ContDownLatch CyclicBarrier Semaphore使用記錄

技術標籤:JUCjava

水平有限,只記錄使用方法和個人理解,不保證全對

閒聊

  • 第一次學習到JUC時候應該是19年,JUC包下的AQS和CAS 併發容器 相關的類都瞭解了一遍。當時想記錄下來但是又覺得理解不夠深刻。日常工作中又很少會用到相關的知識。所以一直擱淺到現在。中間陸陸續續遺忘又撿起。
  • 慢慢的發現如果只有學習沒有產出,學習的意義好像就是當你遇到一個場景會想到有這種解決方案,然後繼續去找這方面的東西重新補充。
  • 所以試著以當下的狀態去敘述這些東西,也算是以後再拿起的索引

ContDownLatch

  • ContDownLatch是一個同步輔助類,用於阻塞一個或者多個執行緒的工具類。多用於阻塞當前主執行緒執行,等待字執行緒的執行結果。
  • 當所有執行緒準備就緒,才開始執行
  • 建立時候指定初始值,每一個執行緒執行完後呼叫countDown方法來表述自己任務執行完畢,所有執行緒執行完之後,被await方法阻塞的執行緒開始繼續執行
  • 1+2+3+4 的計算,同時兩個執行緒去計算一個執行緒執行1+2 另一個執行緒執行 3+ 4 再將子執行緒的結果累加,所以主執行緒是需要子執行緒的執行結果,需要等子執行緒都執行完畢之後在繼續執行的。
  • 比如打英雄聯盟的時候十個人都準備好了,然後就開始團戰。
public class CountDownLatchDemo {

    private static final int THREAD_COUNT = 10;

    private static final CountDownLatch countDownLatch = new CountDownLatch(THREAD_COUNT);

    private static final ExecutorService executorService = ThreadUtil.newExecutor(THREAD_COUNT, THREAD_COUNT * 2);

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

        for (int i = 0; i < 10; i++) {
            final int threadNum = i;
            executorService.execute(() -> {
                try {
                    ready(threadNum);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        countDownLatch.await();// 主執行緒阻塞,等待所有子執行緒到達屏障
        executorService.shutdown();
        System.out.println("開打!開打!");
    }

    private static void ready(int threadNum) throws Exception {
        Thread.sleep(1000);// 補充狀態,技能冷卻
        System.out.println("英雄" + threadNum + "準備就緒");
        countDownLatch.countDown();// 執行緒到達屏障
    }
}

在這裡插入圖片描述

CyclicBarrier

  • CyclicBarrier和CountDownLatch 有很多相似之處,首先都是基於計數器實現,但是有著比CountDownLatch 更多的應用場景和更強大的功能。
  • 可以使多個執行緒之間相互等待,只有等所有的執行緒都執行完畢之後才會繼續往下執行
  • 當呼叫await()時執行緒會進入等待,等待的執行緒數到達我們設定的初始值後,執行緒繼續執行
  • 當CyclicBarrier釋放執行緒後可以重用所以也稱之為可迴圈柵格
  • 玩過英雄聯盟的人應該都知道出門慢輸一半,所以一波公平的團戰,應該等大家都準備就緒才開始,這才是競技精神。
  • 所以正常的團戰邏輯應該是大家回家補充狀態,等待技能冷卻,然後回到中路,開始團戰,活下來的人才有資格補刀。
public class CyclicBarrierDemo {
    private static final int THREAD_COUNT = 10;

    // 十個英雄,主執行緒需要等待所有子執行緒所以 10 + 1
    private final static CyclicBarrier cyclicBarrier = new CyclicBarrier(11);

    private static final ExecutorService executorService = ThreadUtil.newExecutor(THREAD_COUNT, THREAD_COUNT * 2);

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

        for (int a = 0; a < 2; a++) {
            for (int i = 0; i < 10; i++) {
                final int threadNum = i;
                final int threadCou = a;
                executorService.execute(() -> {
                    try {
                        ready(threadNum, threadCou);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                });
            }
            // 需要等所有的英雄都準備就緒
            cyclicBarrier.await();
            if(a == 0){
                System.out.println("所有英雄準備就緒,準備出發");
            }
        }
        System.out.println("開打!開打!");
        executorService.shutdown();
    }

    private static void ready(int threadNum, int threadCou) throws Exception {
        Thread.sleep(1000);// 補充狀態,技能冷卻
        if(threadCou == 0){
            System.out.println("英雄" + threadNum + "準備就緒");
        }else {
            System.out.println("英雄" + threadNum + "到達戰場");
        }
        cyclicBarrier.await();
    }
    
}

在這裡插入圖片描述

Semaphore

  • Semaphore可以控制一個資源可以同時被訪問的次數。
  • 比如在資料庫連線池中的時候用
public class SemaphoreDemo {

    private static final int THREAD_COUNT = 4;

    private static final Semaphore semaphore = new Semaphore(2);

    private static final ExecutorService executorService = ThreadUtil.newExecutor(THREAD_COUNT, THREAD_COUNT * 2);

    public static void main(String[] args) {
        for (int i = 0; i < 4; i++) {
            final int threadNum = i;
            executorService.execute(() -> {
                try {
                    if (semaphore.availablePermits() == 0) {
                        System.out.println(threadNum + "----->> 等待獲取連線");
                    }
                    semaphore.acquire();// 獲取憑證
                    ready(threadNum);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        executorService.shutdown();
    }

    private static void ready(int threadNum) throws Exception {
        System.out.println(threadNum + "連線被獲取");
        Thread.sleep(2000); // sql執行需要時間
        System.out.println("sql執行完畢" + threadNum + "歸還");
        semaphore.release();// 釋放憑證
    }

}

在這裡插入圖片描述