Java多執行緒(7):JUC(上)

2022-11-01 09:01:24

您好,我是湘王,這是我的部落格園,歡迎您來,歡迎您再來~

 

前面把執行緒相關的生命週期、關鍵字、執行緒池(ThreadPool)、ThreadLocal、CAS、鎖和AQS都講完了,現在就剩下怎麼來用多執行緒了。而要想用好多執行緒,其實是可以取一些巧的,比如JUC(好多面試官喜歡問的JUC,就是現在要講的JUC)。JUC就是java.util.concurrent的首字母縮寫,它是Java並行工具包就是中提供的各種工具類的統稱,主要分為幾大類:

1、同步器;

2、執行緒安全的容器;

3、阻塞佇列;

4、一些特殊的類。

他們都有各自適合應用場景。這裡是並行工具包相關類的繼承結構:

 

 

 

 

 

下面從同步器開始。

常用的JUC同步器有四個:

1、CountDownLatch:字面意思是倒計時鎖,如果有「倒計時」的需求,那麼CountDownLatch是最好的工具。它還有一個別稱:發令槍。可以想象一下,火箭點火發射的時候,所有裝置、部門都會依次檢查確認,如果全部都確認準備好了才能開始發射,也就是等倒數到指定的數位(一般是0)的時候,就開始執行預設動作;

2、Semaphore:字面意思號誌,好比紅綠燈,或者就餐排隊時餐館發的數位序號,一次只允許若干個執行緒執行。這個在昨天的例子裡面也已經演示過了,而且還是通過自定義AQS來實現的(號誌可能不太好理解,我更傾向於叫它搖號器);

3、CyclicBarrier:字面意思是屏障或者柵欄,與CountDownLatch比較像,但它側重於工作本身,即指定的若干個工作都滿足考核標準(某個屏障)之後,才能繼續進行下面的工作,且可反覆使用;

4、Exchanger:用於執行緒之間交換資料,更形象地說法是「交換機」,即當一個執行緒完成某項工作後想與另一個執行緒交換資料,就可以使用這個工具類。

下面來一個個地演示它們的用法。

 

一、CountDownLatch

CountDownLatch的功能如果用圖來表示的話,就會是這樣的:

 

 

 

 

CountDownLatch範例程式碼:

/**
 * 發令槍
 *
 * @author 湘王
 */
public class CountDownLatchTester implements Runnable {
    static final CountDownLatch latch = new CountDownLatch(10);
    @Override
    public void run() {
        // 檢查任務
        try {
            System.out.println(Thread.currentThread().getName() + " 檢查完畢!");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            latch.countDown();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executor = Executors.newFixedThreadPool(5);
        for (int i = 10; i > 0; i--) {
            Thread.sleep(1000);
            executor.submit(new CountDownLatchTester());
            System.out.println(i);
        }

        Thread.sleep(1000);
        // 檢查
        latch.await();

        System.out.println();
        System.out.println("點火,發射!");
        // 關閉執行緒池
        executor.shutdown();
    }
}

 

 

執行CountDownLatch的效果是:

 

 

 

 

Semaphore

Semaphore的功能如果用圖來表示的話,就會是這樣的:

 

 

 

 

 

Semaphore範例程式碼:

 

/**
 * 號誌(搖號器)
 *
 * @author 湘王
 */
public class SemaphoreTester implements Runnable {
    static final Semaphore semaphore = new Semaphore(3);

    @Override
    public void run() {
        try {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName() + " 開始進餐");
            Thread.sleep(1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        semaphore.release();
    }

    public static void main(String[] args) {
        ExecutorService excutor = Executors.newFixedThreadPool(15);
        for (int i = 0; i < 15; i++) {
            excutor.submit(new SemaphoreTester());
        }
        excutor.shutdown();
    }
}

 

 

Semaphore執行後的效果是:

 

 

 

 

 

CyclicBarrier

CyclicBarrier的功能如果用圖來表示的話,就會是這樣的:

 

 

 

 

 

CyclicBarrier範例程式碼:

/**
 * 柵欄
 *
 * @author 湘王
 */
public class CyclicBarrierTester implements Runnable {
    private final static CyclicBarrier barrier = new CyclicBarrier(3);

    @Override
    public void run() {
        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " 已達到預定位置,等待指令...");
            // 只有最後一個執行緒執行後,所有的執行緒才能執行2
            barrier.await();
            Thread.sleep(1000);
            // 2 所有執行緒都會執行的動作
            System.out.println(Thread.currentThread().getName() + " 已突破第一道封鎖線");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(3);
        for (int i = 0; i < 15; i++) {
            executor.submit(new CyclicBarrierTester());
        }
        // 關閉執行緒池
        executor.shutdown();
    }
}

 

 

CyclicBarrier執行後的效果是:

 

 

 

Exchanger

Exchanger的功能如果用圖來表示的話,就會是這樣的:

 

 

 

 

 

Exchanger範例程式碼:

 

/**
 * 交換機
 *
 * @author 湘王
 */
public class ExchangerTester implements Runnable {
    Exchanger<Object> exchanger = null;
    Object object = null;

    public ExchangerTester(Exchanger<Object> exchanger, Object object) {
        this.exchanger = exchanger;
        this.object = object;
    }

    @Override
    public void run() {
        try {
            Object previous = this.object;
            this.object = this.exchanger.exchange(this.object);
            System.out.println(Thread.currentThread().getName() + " 用物件 " + previous + " 換物件 " + this.object);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        Exchanger<Object> exchanger = new Exchanger<Object>();
        ExchangerTester tester1 = new ExchangerTester(exchanger, "A");
        ExchangerTester tester2 = new ExchangerTester(exchanger, "B");

        new Thread(tester1).start();
        new Thread(tester2).start();
    }
}

 

 

Exchanger執行後的效果是:

 

 

 

 

把這四種同步器掌握好(包括它們的組合使用),幾乎可以解決90%以上的使用多執行緒的場景問題,再也不用擔心不會多執行緒了。

 

 


 

 

感謝您的大駕光臨!諮詢技術、產品、運營和管理相關問題,請關注後留言。歡迎騷擾,不勝榮幸~