一、概念理解
Exchanger的作用就是為了兩個線程之間交換數據,他提供了一個內部方法exchange,這個內部方法就好比是一個同步點,只有兩個方法都到達同步點,才可以交換數據。我們換一張圖來演示一波。
也就是說只有線程A和線程B都到達同步點,才可以交換數據。
我們上代碼直接看看如何使用,然後再去看看使用的時候需要注意什麼。
二、使用案例
1、基本使用
首先我們定義一個測試類ExchangerTest:
public class ExchangerTest { private static Exchanger<String> exchanger = new Exchanger<>(); private static String threadA_data = "100塊"; private static String threadB_data = "50塊"; public static void main(String[] args) { new ThreadA(exchanger, threadA_data).start(); new ThreadB(exchanger, threadB_data).start(); } }
在這個類中,我們使用了ThreadA和ThreadB兩個線程交換數據,然後我們定義了一個交換器Exchanger來交換。下面我們看看這倆線程是如何實現的。
public class ThreadA extends Thread { private Exchanger<String> exchanger = new Exchanger<>(); private String data = null; public ThreadA(Exchanger<String> exchanger, String data) { this.exchanger = exchanger; this.data = data; } @Override public void run() { try { TimeUnit.SECONDS.sleep(3); System.out.println("線程A交換前的數據是:"+data); data = exchanger.exchange(data); System.out.println("線程A交換後的數據是:"+data); } catch (InterruptedException e) { e.printStackTrace(); } } }
在這裡我們主要是看run方法的實現,首先我們打印出交換之前的數據信息,然後使用交換器交換數據,最後再打印出交換之後的數據。由於ThreadB和ThreadA實現方式一樣,在這裡我們只給出一份代碼即可。下面我們就可以運行一下,看看測試結果:
現在我們看到,線程A和線程B就可以正常的進行交換了。通過這個案例我們會發現,Exchanger使用起來真的是超級簡單。不過看起來很簡單,其實還挖了很多的坑,下面我們來看看。
注意點一:兩個線程最終必須到達同步點
這是什麼意思呢?我們畫一張圖,舉一個例子。
上面這張圖的意思是這個樣子的,左邊的線程還有20秒才可以到達同步點,但是右邊的線程設置了超時時間,如果10秒鐘後對方沒有到達,那麼這次交易就宣告失敗。對於我們的程序來說也會出現異常。我們代碼演示一下:
首先這次我們看右邊的線程A:設置了超時時間為10秒
public class ThreadA extends Thread { private Exchanger<String> exchanger = new Exchanger<>(); private String data = null; public ThreadA(Exchanger<String> exchanger, String data) { this.exchanger = exchanger; this.data = data; } @Override public void run() { try { TimeUnit.SECONDS.sleep(3); System.out.println("線程A交換前的數據是:"+data); //線程A:設置超時時間為10秒,對應於右邊的線程 data = exchanger.exchange(data,10,TimeUnit.SECONDS); System.out.println("線程A交換後的數據是:"+data); } catch (InterruptedException | TimeoutException e) { e.printStackTrace(); } } }
然後就是左邊的線程B:還需要20秒才可以抵達
public class ThreadB extends Thread { private Exchanger<String> exchanger = new Exchanger<>(); private String data = null; public ThreadB(Exchanger<String> exchanger, String data) { this.exchanger = exchanger; this.data = data; } @Override public void run() { try { //我還有20秒才可以抵達 TimeUnit.SECONDS.sleep(20); System.out.println("線程B交換後的數據hashcode是:"+data.hashCode()); data = exchanger.exchange(data); System.out.println("線程B交換後的數據hashcode是:"+data.hashCode()); } catch (InterruptedException e) { e.printStackTrace(); } } }
現在我們再去測試一下看看會出現什麼結果:
我們發現線程A等待了10秒之後,線程B還沒有到達,那就宣告交易失敗。程序出現超時異常。
注意點二:交換的線程必須成對出現
這個注意點是什麼意思呢?其實就是不能是單,就好比是找對象,最後總是成雙成對的,要是5個男的4個女的,那剩下的一個男同胞怎麼辦,只能在那傻等了。這個我們也可以代碼測試一下,只是新增了一個線程C。測試代碼變一下:
public class ExchangerTest3 { private static Exchanger<String> exchanger = new Exchanger<>(); private static String threadA_data = "100塊"; private static String threadB_data = "50塊"; private static String threadC_data = "10塊"; public static void main(String[] args) { new ThreadA(exchanger, threadA_data).start(); new ThreadB(exchanger, threadB_data).start(); new ThreadC(exchanger, threadC_data).start(); } }
此時我們再去測試,就會發現,總有一個線程處於死循環一直等待的狀態。
注意點三:多個線程交換數據
上面我們提到了交換的線程配對之後不能落單,那麼如果此時有多個成對的線程了,誰和誰配對呢?答案我們先告訴你,那就是胡亂配對。
在這裡我們在注意點二的基礎之上繼續增加一個線程D,然後繼續更改我們的測試類運行一下:
對於Exchanger的使用基本上需要注意的就是這麼多。希望對你有幫助。