Java執行緒--Exchanger交換器
Exchanger<T>交換器
目錄
Exchanger<T>概念
上圖業務場景描述:A有球,想要和B的魚互動,整個互動過程描述如下:
A在準備素材,球,準備的比較快,然後就發出交換訴求,exchange(球),發現B還沒帶著魚來,就阻塞在這裡。B這時還在釣魚,等B釣到魚後,立馬發出交換訴求,exchange(魚),發現A阻塞在那裡了,就把A喚醒,然後兩人互換了東西,A擁有了魚,B擁有了球。
A synchronization point at which threads can pair and swap elements within pairs. Each thread presents some object on entry to the exchange method, matches with a partner thread, and receives its partner's object on return. An Exchanger may be viewed as a bidirectional form of a SynchronousQueue. Exchangers may be useful in applications such as genetic algorithms and pipeline designs.
在以上的描述中,有幾個要點:
- 交換值是同步的;
- 成對的執行緒之間交換資料;
- 可看成是雙向的同步佇列;
- 可應用於演演算法、流水線設計;
Exchanger<V>類中的主要方法就是:exchange(V x)方法,成對的兩個執行緒之間,都呼叫了該方法,就能在兩個執行緒彼此都準備好資料後,成功的交換資料給對方,然後各自返回。如果想支援成對的兩個執行緒之間,一個沒耐性,等的時間過長,或者被打斷了就不交換資料了,可以使用exchange(V x, long timeout, TimeUnit unit)方法。
Exchanger<T>示例
import java.util.concurrent.*;
import java.util.concurrent.locks.*;
import java.util.concurrent.atomic.*;
//球執行緒
class BallTask implements Runnable
{
private Exchanger<String> e;
public BallTask(Exchanger<String> e){
this.e = e;
}
public void run(){
try{
long sleepTime = (long)(Math.random() * 2500) ;
String tName = Thread.currentThread().getName();
System.out.println(tName+"正在買球,用時["+sleepTime+"]才買到球,趕緊去換魚...");
Thread.sleep(sleepTime);
String str = e.exchange(tName+":的球");
System.out.println("【"+tName+":的球】換到了-->【"+str+"】");
}
catch(Exception e){
}
finally{
}
}
}
//魚執行緒
class FishTask implements Runnable
{
private Exchanger<String> e;
public FishTask(Exchanger<String> e){
this.e = e;
}
public void run(){
try{
long sleepTime = (long)(Math.random() * 2500) ;
String tName = Thread.currentThread().getName();
System.out.println(tName+"正在釣魚,用時["+sleepTime+"]才釣到魚,趕緊去換球...");
Thread.sleep(sleepTime);
String str = e.exchange(tName+":的魚");
System.out.println("【"+tName+":的魚】換到了-->【"+str+"】");
}
catch(Exception e){
}
finally{
}
}
}
public class ExchangerTest
{
public static void main(String[] args)
{
Exchanger<String> e = new Exchanger<String>();
BallTask bTask = new BallTask(e); //任務:球執行緒
FishTask fTask = new FishTask(e); //任務:魚執行緒
Thread bThread = new Thread(bTask,"Ball");
Thread fThread = new Thread(fTask,"Fish");
bThread.start();
fThread.start();
System.out.println("我是主執行緒,準備看看你們交易情況...\n\r");
try{
bThread.join();
fThread.join();
}catch(Exception ep){}
System.out.println("\n\r我是主執行緒,已看到你們的交易結果...");
}
}
程式執行結果如下:
我是主執行緒,準備看看你們交易情況...
Ball正在買球,用時[88]才買到球,趕緊去換魚...
Fish正在釣魚,用時[2215]才釣到魚,趕緊去換球...
【Fish:的魚】換到了-->【Ball:的球】
【Ball:的球】 換到了-->【Fish:的魚】
我是主執行緒,已看到你們的交易結果...
Exchanger和Semaphore區別
Exchanger<V>交換器和Semaphore訊號量在關於生產者消費者《產1消1模式》運用的區別:
- Exchanger交換器:成對的兩個執行緒,各個執行緒有各個執行緒的自己資料V,A執行緒擁有V1,B執行緒擁有V2,V1<...>V2互換。
- Semaphore訊號量:成對的兩個執行緒,只需一個數據池即可,生產者生產資料注入資料池,消費者從資料池取走資料消費。
- Exchanger交換器:兩個執行緒之間的通訊僅僅一個Exchanger例項即可。
- Semaphore訊號量:兩個執行緒之間的通訊需要兩個訊號量,生產訊號指示燈,消費訊號指示燈。
- Exchanger和Semaphore的共同點:兩個執行緒之間需要同步通訊。生產的過快,沒用,必須等消費完了,才能進行下一生產1;同理,消費的過快,也沒用,必須等生產完了,才能進行下一消費1。
借花獻佛
摘自部落格(http://brokendreams.iteye.com/blog/2253956)
其實就是”我”和”你”(可能有多個”我”,多個”你”)在一個叫Slot的地方做交易(一手交錢,一手交貨),過程分以下步驟:
1。我先到一個叫做Slot的交易場所交易,發現你已經到了,那我就嘗試喊你交易,如果你迴應了我,決定和我交易那麼進入第2步;如果別人搶先一步把你喊走了,那我就進入第5步。
2。我拿出錢交給你,你可能會接收我的錢,然後把貨給我,交易結束;也可能嫌我掏錢太慢(超時)或者接個電話(中斷),TM的不賣了,走了,那我只能再找別人買貨了(從頭開始)。
3。我到交易地點的時候,你不在,那我先嚐試把這個交易點給佔了(一屁股做凳子上…),如果我成功搶佔了單間(交易點),那就坐這兒等著你拿貨來交易,進入第4步;如果被別人搶座了,那我只能在找別的地方兒了,進入第5步。
4。你拿著貨來了,喊我交易,然後完成交易;也可能我等了好長時間你都沒來,我不等了,繼續找別人交易去,走的時候我看了一眼,一共沒多少人,弄了這麼多單間(交易地點Slot),太TM浪費了,我喊來交易地點管理員:一共也沒幾個人,搞這麼多單間兒幹毛,給哥撤一個!。然後再找別人買貨(從頭開始);或者我老大給我打了個電話,不讓我買貨了(中斷)。
5。我跑去喊管理員,尼瑪,就一個坑交易個毛啊,然後管理在一個更加開闊的地方開闢了好多個單間,然後我就挨個來看每個單間是否有人。如果有人我就問他是否可以交易,如果迴應了我,那我就進入第2步。如果我沒有人,那我就佔著這個單間等其他人來交易,進入第4步。
6。如果我嘗試了幾次都沒有成功,我就會認為,是不是我TM選的這個單間風水不好?不行,得換個地兒繼續(從頭開始);如果我嘗試了多次發現還沒有成功,怒了,把管理員喊來:給哥再開一個單間(Slot),加一個凳子,這麼多人就這麼幾個破凳子夠誰用!