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),加一个凳子,这么多人就这么几个破凳子够谁用!