Exchanger的工作原理及例項
1.實現原理
Exchanger(交換者)是一個用於執行緒間協作的工具類。Exchanger用於進行執行緒間的資料交換。它提供一個同步點,在這個同步點兩個執行緒可以交換彼此的資料。這兩個執行緒通過exchange方法交換資料, 如果第一個執行緒先執行exchange方法,它會一直等待第二個執行緒也執行exchange,當兩個執行緒都到達同步點時,這兩個執行緒就可以交換資料,將本執行緒生產出來的資料傳遞給對方。因此使用Exchanger的重點是成對的執行緒使用exchange()方法,當有一對執行緒達到了同步點,就會進行交換資料。因此該工具類的執行緒物件是成對的。
Exchanger類提供了兩個方法,String exchange(V x):用於交換,啟動交換並等待另一個執行緒呼叫exchange;String exchange(V x,long timeout,TimeUnit unit):用於交換,啟動交換並等待另一個執行緒呼叫exchange,並且設定最大等待時間,當等待時間超過timeout便停止等待。
2.例項講解
通過以上的原理,可以知道使用Exchanger類的核心便是exchange()方法的使用,接下來通過一個例子來使的該工具類的用途更加清晰。該例子主要講解的是前段時間NBA交易截止日的交易。
package concurrent; import java.util.concurrent.Exchanger; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.*; public class ExchangerDemo { public static void main(String[] args) { ExecutorService executor = Executors.newCachedThreadPool(); final Exchanger exchanger = new Exchanger(); executor.execute(new Runnable() { String data1 = "克拉克森,小拉里南斯"; @Override public void run() { nbaTrade(data1, exchanger); } }); executor.execute(new Runnable() { String data1 = "格里芬"; @Override public void run() { nbaTrade(data1, exchanger); } }); executor.execute(new Runnable() { String data1 = "哈里斯"; @Override public void run() { nbaTrade(data1, exchanger); } }); executor.execute(new Runnable() { String data1 = "以賽亞托馬斯,弗萊"; @Override public void run() { nbaTrade(data1, exchanger); } }); executor.shutdown(); } private static void nbaTrade(String data1, Exchanger exchanger) { try { System.out.println(Thread.currentThread().getName() + "在交易截止之前把 " + data1 + " 交易出去"); Thread.sleep((long) (Math.random() * 1000)); String data2 = (String) exchanger.exchange(data1); System.out.println(Thread.currentThread().getName() + "交易得到" + data2); } catch (InterruptedException e) { e.printStackTrace(); } } }
執行程式,得到如下結果:
pool-1-thread-1在交易截止之前把 克拉克森,小拉里南斯 交易出去
pool-1-thread-2在交易截止之前把 格里芬 交易出去
pool-1-thread-3在交易截止之前把 哈里斯 交易出去
pool-1-thread-4在交易截止之前把 以賽亞托馬斯,弗萊 交易出去
pool-1-thread-2交易得到哈里斯
pool-1-thread-3交易得到格里芬
pool-1-thread-4交易得到克拉克森,小拉里南斯
pool-1-thread-1交易得到以賽亞托馬斯,弗萊
以上例子可以看出兩個都呼叫exchange()方法的執行緒會進行交換資料。接下來假設執行緒數目只有奇數個,觀察情況:
如以下程式碼,將第四個執行緒註釋掉。
package concurrent;
import java.util.concurrent.Exchanger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.*;
public class ExchangerDemo {
public static void main(String[] args) {
ExecutorService executor = Executors.newCachedThreadPool();
final Exchanger exchanger = new Exchanger();
executor.execute(new Runnable() {
String data1 = "克拉克森,小拉里南斯";
@Override
public void run() {
nbaTrade(data1, exchanger);
}
});
executor.execute(new Runnable() {
String data1 = "格里芬";
@Override
public void run() {
nbaTrade(data1, exchanger);
}
});
executor.execute(new Runnable() {
String data1 = "哈里斯";
@Override
public void run() {
nbaTrade(data1, exchanger);
}
});
// executor.execute(new Runnable() {
// String data1 = "以賽亞托馬斯,弗萊";
//
// @Override
// public void run() {
// nbaTrade(data1, exchanger);
// }
// });
executor.shutdown();
}
private static void nbaTrade(String data1, Exchanger exchanger) {
try {
System.out.println(Thread.currentThread().getName() + "在交易截止之前把 " + data1 + " 交易出去");
Thread.sleep((long) (Math.random() * 1000));
String data2 = (String) exchanger.exchange(data1);
System.out.println(Thread.currentThread().getName() + "交易得到" + data2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
執行程式,得到如下結果:
pool-1-thread-1在交易截止之前把 克拉克森,小拉里南斯 交易出去
pool-1-thread-2在交易截止之前把 格里芬 交易出去
pool-1-thread-3在交易截止之前把 哈里斯 交易出去
pool-1-thread-3交易得到格里芬
pool-1-thread-2交易得到哈里斯
由結果可知,執行緒2和執行緒3進行了交換資料,而執行緒1一直等待與它交換資料的執行緒呼叫exchange,但是隻有3個執行緒,所以會一直等待。
因此,當兩個執行緒之間出現資料交換的情況,可以使用Exchanger工具類實現資料交換。注意exchange方法的含義,以及觸發資料交換的條件。