面試總結 最近棧長面試了一個 5 年經驗的 Java 程式員,簡歷和個人介紹都提到了精通 Java 多線程,於是我就問了幾個多線程方面的問題: 1、實現多線程有哪幾種方式,如何返回結果? 2、多個線程如何實現順序訪問? 3、兩個線程如何進行數據交換? 4、如何統計 5 個線程的運行總耗時? 5、如何 ...
面試總結
最近棧長面試了一個 5 年經驗的 Java 程式員,簡歷和個人介紹都提到了精通 Java 多線程,於是我就問了幾個多線程方面的問題:
3、兩個線程如何進行數據交換?
大概問了他這幾個問題,答的並不是太好,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 類的使用,只能用於多個線程中的兩個線程兩兩交換數據,如果沒有對應的線程交換就會一直阻塞,可設置超時,可以中斷。
你都掌握了嗎?面試如果問到,你需要掌握這個類的用法,當然也有其他的方案,但如果不是必須就沒有必要重覆造輪子,重覆造輪子需要考慮的面更多。
本文所有案例源代碼已經上傳:
歡迎 Star 學習,後面 Java 示例都會在這上面提供!
好了,今天的分享就到這裡了,後面棧長會分享更多好玩的 Java 技術和最新的技術資訊,關註公眾號Java技術棧第一時間推送,我也將主流 Java 面試題和參考答案都整理好了,在公眾號後臺回覆關鍵字 "面試" 進行刷題。
最後,覺得我的文章對你用收穫的話,動動小手,給個在看、轉發,原創不易,棧長需要你的鼓勵。
版權聲明: 本文系公眾號 "Java技術棧" 原創,轉載、引用本文內容請註明出處,抄襲、洗稿一律投訴侵權,後果自負,並保留追究其法律責任的權利。
近期熱文推薦:
1.1,000+ 道 Java面試題及答案整理(2022最新版)
4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!
覺得不錯,別忘了隨手點贊+轉發哦!