面了個 5 年 Java,兩個線程進行數據交換都不會,我真是醉了。。

面試總結

最近棧長面試了一個 5 年經驗的 Java 程序員,簡歷和個人介紹都提到了精通 Java 多線程,於是我就問了幾個多線程方面的問題:

1、實現多線程有哪幾種方式,如何返回結果?

2、多個線程如何實現順序訪問?

3、兩個線程如何進行數據交換?

4、如何統計 5 個線程的運行總耗時?

5、如何將一個任務拆分成多個子任務執行,最後合併結果?

大概問了他這幾個問題,答的並不是太好,3、4、5 題都沒有真正答上來,其實這幾個問題在 JDK 包中都有答案,但他給的是他個人臨時思考的方案,而且我個人覺得可能行不通。

工作 5 年了,這幾個題都答不好,有點說不過去,我真是醉了。。

其中,1、2、4、5 題我都在公衆號Java技術棧分享過相關的教程,也都在Java面試庫小程序上整理好了,最近面試的看看,今天就分享一下第 3 題的參考答案。

第 3 題也是通過 JDK 中的 java.util.concurrent.Exchanger 類來實現的,並不需要我們重複造輪子,這個工具類在 JDK 1.5 中就已經引入了,並不是什麼 "新特性"。

Exchanger 簡介

Exchanger 就是線程之間的數據交換器,只能用於兩個線程之間的數據交換。

Exchanger 提供了兩個公開方法:

1、只帶泛型 V(交換的數據對象)的方法,線程一直阻塞,直到其他任意線程和它交換數據,或者被線程中斷;線程中斷也是一門學問,棧長在公衆號Java技術棧已經分享過,可在公衆號搜索閱讀;

2、另外一個帶時間的方法,如果超過設置時間還沒有線程和它交換數據,就會拋出 TimeoutException 異常;

Exchanger 實戰

簡單數據交換

來一個兩個線程正常數據交換的簡單示例:

private static void test1() {
    Exchanger exchanger = new Exchanger();

    new Thread(() -> {
        try {
            Object data = "-公衆號Java技術棧AAA";
            System.out.println(Thread.currentThread().getName() + data);

            // 開始交換數據
            data = exchanger.exchange(data);
            System.out.println(Thread.currentThread().getName() + data);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();

    new Thread(() -> {
        try {
            Object data = "-公衆號Java技術棧BBB";
            System.out.println(Thread.currentThread().getName() + data);

            // 開始交換數據
            data = exchanger.exchange(data);
            System.out.println(Thread.currentThread().getName() + data);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
}

這段代碼的邏輯:

1、創建並啓動兩個線程;

2、進行數據交換前先打印出自己線程的數據;

3、進行數據交換;

4、打印數據交換之後的數據;

輸出結果:

從結果可以看出,線程 0、1 分別先打印出 A、B,數據交換之後,打印出了 B、A,數據交換正常!

超時數據交換

上面演示了兩個線程的正常交換,下面再來一個帶超時的示例:

private static void test2() {
    Exchanger exchanger = new Exchanger();

    new Thread(() -> {
        try {
            Object data = "-公衆號Java技術棧AAA";
            System.out.println(Thread.currentThread().getName() + data);

            // 開始交換數據
            data = exchanger.exchange(data, 3000L, TimeUnit.MILLISECONDS);
            System.out.println(Thread.currentThread().getName() + data);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();
}

現在只啓動了一個線程,並且設置了超時時間 3 秒。

輸出結果:

首先線程輸出了自己的數據,然後 3 秒後,並沒有其他線程和它交換數據,所以拋出了超時異常,最後線程結束運行。

本文所有案例源代碼已經上傳:https://github.com/javastacks/javastack

中斷數據交換

線程開始交換數據後,會一直阻塞直到其他任意線程和它交換數據,或者被中斷、超時,上面演示了超時,下面這個示例演示一下中斷。

private static void test3() {
    Exchanger exchanger = new Exchanger();

    new Thread(() -> {
        try {
            Object data = "-公衆號Java技術棧AAA";
            System.out.println(Thread.currentThread().getName() + data);

            // 開始交換數據
            data = exchanger.exchange(data);
            System.out.println(Thread.currentThread().getName() + data);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();
}

結果輸出:

默認情況下不帶超時設置會一直阻塞運行中……

現在我再加入一段中斷的邏輯:

private static void test3() throws InterruptedException {
    Exchanger exchanger = new Exchanger();

    Thread thread = new Thread(() -> {
        try {
            Object data = "-公衆號Java技術棧AAA";
            System.out.println(Thread.currentThread().getName() + data);

            // 開始交換數據
            data = exchanger.exchange(data);
            System.out.println(Thread.currentThread().getName() + data);
        } catch (Exception e) {
            e.printStackTrace();
        }
    });

    thread.start();

    // 線程中斷
    Thread.sleep(3000L);
    thread.interrupt();
}

主線程休眠 3 秒後,中斷該線程。

輸出結果:

輸出結果 3 秒後,線程被中斷了,拋出了中斷異常,線程也停止阻塞,最後線程結束運行。

兩兩數據交換

另外需要知道是,Exchanger 只能用於兩個線程之間的數據交換,一個線程開啓數據交換之後,會阻塞直到其他任意線程同樣開啓數據交換達到交換點。

最後來個示例,開啓 10 個線程,看它們是怎麼兩兩交換的:

private static void test4() {
    Exchanger exchanger = new Exchanger();

    for (int i = 1; i <= 10; i++) {
        Integer data = i;
        new Thread(() -> {
            try {
                Object exchange = exchanger.exchange(data);
                System.out.println(Thread.currentThread().getName() + "-" + exchange);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "Java技術棧" + i).start();
    }
}

輸出結果:

可以看到,10 個線程,都兩兩交換彼此的數據了。

總結

本文介紹了線程之間的數據交換器 Exchanger 類的使用,只能用於多個線程中的兩個線程兩兩交換數據,如果沒有對應的線程交換就會一直阻塞,可設置超時,可以中斷。

你都掌握了嗎?面試如果問到,你需要掌握這個類的用法,當然也有其他的方案,但如果不是必須就沒有必要重複造輪子,重複造輪子需要考慮的面更多。

本文所有案例源代碼已經上傳:

https://github.com/javastacks/javastack

歡迎 Star 學習,後面 Java 示例都會在這上面提供!

好了,今天的分享就到這裏了,後面棧長會分享更多好玩的 Java 技術和最新的技術資訊,關注公衆號Java技術棧第一時間推送,我也將主流 Java 面試題和參考答案都整理好了,在公衆號後臺回覆關鍵字 "面試" 進行刷題。

最後,覺得我的文章對你用收穫的話,動動小手,給個在看、轉發,原創不易,棧長需要你的鼓勵。

版權聲明: 本文系公衆號 "Java技術棧" 原創,轉載、引用本文內容請註明出處,抄襲、洗稿一律投訴侵權,後果自負,並保留追究其法律責任的權利。

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這纔是優雅的方式!!

5.《Java開發手冊(嵩山版)》最新發布,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章