在好久以前我曾寫過一篇一篇文章介紹線程間如何進行通訊的問題,當時使用的是等待通知模型,這篇文章介紹一個java提供的用於兩個線程間通訊的工具類Exchanger。java
Exchanger的做用就是爲了兩個線程之間交換數據,他提供了一個內部方法exchange,這個內部方法就比如是一個同步點,只有兩個方法都到達同步點,才能夠交換數據。咱們換一張圖來演示一波。ide
也就是說只有線程A和線程B都到達同步點,才能夠交換數據。工具
咱們上代碼直接看看如何使用,而後再去看看使用的時候須要注意什麼。測試
一、基本使用this
首先咱們定義一個測試類ExchangerTest:spa
1public class ExchangerTest {
2 private static Exchanger<String> exchanger = new Exchanger<>();
3 private static String threadA_data = "100塊";
4 private static String threadB_data = "50塊";
5 public static void main(String[] args) {
6 new ThreadA(exchanger, threadA_data).start();
7 new ThreadB(exchanger, threadB_data).start();
8 }
9}
在這個類中,咱們使用了ThreadA和ThreadB兩個線程交換數據,而後咱們定義了一個交換器Exchanger來交換。下面咱們看看這倆線程是如何實現的。線程
1public class ThreadA extends Thread {
2 private Exchanger<String> exchanger = new Exchanger<>();
3 private String data = null;
4 public ThreadA(Exchanger<String> exchanger, String data) {
5 this.exchanger = exchanger;
6 this.data = data;
7 }
8 @Override
9 public void run() {
10 try {
11 TimeUnit.SECONDS.sleep(3);
12 System.out.println("線程A交換前的數據是:"+data);
13 data = exchanger.exchange(data);
14 System.out.println("線程A交換後的數據是:"+data);
15 } catch (InterruptedException e) {
16 e.printStackTrace();
17 }
18 }
19}
在這裏咱們主要是看run方法的實現,首先咱們打印出交換以前的數據信息,而後使用交換器交換數據,最後再打印出交換以後的數據。因爲ThreadB和ThreadA實現方式同樣,在這裏咱們只給出一份代碼便可。下面咱們就能夠運行一下,看看測試結果:code
如今咱們看到,線程A和線程B就能夠正常的進行交換了。經過這個案例咱們會發現,Exchanger使用起來真的是超級簡單。不過看起來很簡單,其實還挖了不少的坑,下面咱們來看看。orm
這是什麼意思呢?咱們畫一張圖,舉一個例子。對象
上面這張圖的意思是這個樣子的,左邊的線程還有20秒才能夠到達同步點,可是右邊的線程設置了超時時間,若是10秒鐘後對方沒有到達,那麼此次交易就宣告失敗。對於咱們的程序來講也會出現異常。咱們代碼演示一下:
首先此次咱們看右邊的線程A:設置了超時時間爲10秒
1public class ThreadA extends Thread {
2 private Exchanger<String> exchanger = new Exchanger<>();
3 private String data = null;
4 public ThreadA(Exchanger<String> exchanger, String data) {
5 this.exchanger = exchanger;
6 this.data = data;
7 }
8 @Override
9 public void run() {
10 try {
11 TimeUnit.SECONDS.sleep(3);
12 System.out.println("線程A交換前的數據是:"+data);
13 //線程A:設置超時時間爲10秒,對應於右邊的線程
14 data = exchanger.exchange(data,10,TimeUnit.SECONDS);
15 System.out.println("線程A交換後的數據是:"+data);
16 } catch (InterruptedException | TimeoutException e) {
17 e.printStackTrace();
18 }
19 }
20}
而後就是左邊的線程B:還須要20秒才能夠抵達
1public class ThreadB extends Thread {
2 private Exchanger<String> exchanger = new Exchanger<>();
3 private String data = null;
4 public ThreadB(Exchanger<String> exchanger, String data) {
5 this.exchanger = exchanger;
6 this.data = data;
7 }
8 @Override
9 public void run() {
10 try {
11 //我還有20秒才能夠抵達
12 TimeUnit.SECONDS.sleep(20);
13 System.out.println("線程B交換後的數據hashcode是:"+data.hashCode());
14 data = exchanger.exchange(data);
15 System.out.println("線程B交換後的數據hashcode是:"+data.hashCode());
16 } catch (InterruptedException e) {
17 e.printStackTrace();
18 }
19 }
20}
如今咱們再去測試一下看看會出現什麼結果:
咱們發現線程A等待了10秒以後,線程B尚未到達,那就宣告交易失敗。程序出現超時異常。
這個注意點是什麼意思呢?其實就是不能是單,就比如是找對象,最後老是成雙成對的,要是5個男的4個女的,那剩下的一個男同胞怎麼辦,只能在那傻等了。這個咱們也能夠代碼測試一下,只是新增了一個線程C。測試代碼變一下:
1public class ExchangerTest3 {
2 private static Exchanger<String> exchanger = new Exchanger<>();
3 private static String threadA_data = "100塊";
4 private static String threadB_data = "50塊";
5 private static String threadC_data = "10塊";
6 public static void main(String[] args) {
7 new ThreadA(exchanger, threadA_data).start();
8 new ThreadB(exchanger, threadB_data).start();
9 new ThreadC(exchanger, threadC_data).start();
10 }
11}
此時咱們再去測試,就會發現,總有一個線程處於死循環一直等待的狀態。
上面咱們提到了交換的線程配對以後不能落單,那麼若是此時有多個成對的線程了,誰和誰配對呢?答案咱們先告訴你,那就是胡亂配對。
在這裏咱們在注意點二的基礎之上繼續增長一個線程D,而後繼續更改咱們的測試類運行一下:
對於Exchanger的使用基本上須要注意的就是這麼多。但願對你有幫助。