摘要:JDK1.5以后在JUC包下提供的Exchanger类,可以用来实现两个线程之间的信息交换。本文分享自华为云社区《一行Java代码实现两玩家交换装备【并发编程】》,作者:陈皮之JavaLib。1什么是交换器?从JDK1.5开始JUC包下提供的Exchanger类可以用来在两个线程之间交换信息。Exchanger对象可以理解为一个包含2个网格的容器,通过调用exchanger方法向网格填充信息,当两个网格都填充了信息后,两个网格中的信息会自动交换,交换后的信息将返回给调用线程,从而实现两个线程之间的信息交换。功能看似简单,但在某些场景下非常有用,比如游戏中两名玩家交换装备;男女相亲的交友软件。下面简单模拟一下两个玩家交换装备的场景。packagecom.chenpi;importjava.util.concurrent.Exchanger;/***@Description*@AuthorChenpi*@Date2021/7/11*@Version1.0*/publicclassChenPiMain{publicstaticvoidmain(String[]args)throwsInterruptedException{Exchangerexchanger=newExchanger<>();newThread(()->{Stringstr=null;try{str=exchanger.exchange("龙剑");}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("交易成功,"+Thread.currentThread().getName()+"Get"+str);},"周芷若").start();newThread(()->{Stringstr=null;try{str=exchanger.exchange("易天剑");}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("交易成功,"+Thread.currentThread().getName()+"获取"+str);},"张无忌").start();}}//输出结果如下:交易成功,张无忌得到神龙剑,周芷若得到倚天剑详解2ExchangerExchager类可以用来实现两个线程之间的信息交换。如果一个线程调用了Exchanger对象的exchange方法,它会一直阻塞Plug直到另一个线程与其交换信息,并将交换的信息返回给调用线程,从而实现两个线程之间的信息交换。Exchager底层同样采用了spin和cas机制。注意,如果两个以上的线程调用同一个Exchanger对象的exchange方法,结果是不可预知的。只要两个线程满足条件,就认为匹配成功,交换信息。剩余配对失败的线程将被阻塞等待,直到另一个线程可以与之配对配对。packagecom.chenpi;importjava.util.concurrent.Exchanger;/***@Description*@AuthorChenpi*@Date2021/7/11*@Version1.0*/publicclassChenPiMain{publicstaticvoidmain(String[]args){Exchangerexchanger=newExchanger<>();newThread(()->{Stringstr=null;try{str=exchanger.exchange("龙剑");}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("交易成功,"+Thread.currentThread().getName()+"Get"+str);},"周芷若").start();newThread(()->{Stringstr=null;try{str=exchanger.exchange("易天剑");}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("交易成功了,"+Thread.currentThread().getName()+"Get"+str);},"张无忌").start();newThread(()->{Stringstr=null;try{str=exchanger.exchange("假倚天剑");}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("交易成功,"+Thread.currentThread().getName()+"Get"+str);},"程坤").start();}}//输出结果如下:交易成功,周芷若拿到假倚天剑,程坤拿到神龙剑。当然,等待交换信息的线程是可以中断的。比如玩家在等待交易的时候,玩家突然下线了,那么线程应该中断等待packagecom.chenpi;importjava.lang.Thread.State;importjava.util.ArrayList;importjava.util.List;importjava.util.concurrent.Exchanger;/***@Description*@Author陈皮*@Date2021/7/11*@Version1.0*/publicclassChenPiMain{publicstaticvoidmain(String[]args)throwsInterruptedException{Exchangerexchanger=newExchanger<>();列表<线程>threads=newArrayList<>(3);Threadthread1=newThread(()->{Stringstr=null;try{str=exchanger.exchange("屠龙刀");}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("交易成功,"+Thread.currentThread().getName()+"获得"+str);},"周芷若");threads.add(thread1);Threadthread2=newThread(()->{Stringstr=null;try{str=exchanger.exchange("倚天剑");}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("交易成功,"+Thread.currentThread().getName()+"获取"+str);},"张无忌");threads.add(thread2);Threadthread3=newThread(()->{Stringstr=null;try{str=exchanger.exchange("FakeDragonSaber");}catch(InterruptedExceptione){e.printStackTrace();}System.out.println("交易成功,"+Thread.currentThread().getName()+"Get"+str);},"程坤");threads.add(thread3);for(Threadthread:threads){thread.start();}//等待5秒Thread.sleep(5000);for(Threadthread:threads){System.out.println(thread.getName()+":"+thread.getState());//如果仍然阻塞等待则中断线程if(thread.getState()==State.WAITING){thread.interrupt();}}}}//输出结果如下:交易成功,张无忌得到神龙剑,周芷若得到倚天剑周芷若:TERMINATED张无忌:TERMINATED程坤:WAITING交易成功,程坤在com.chenpi.ChenPiMain.lambda$main$2(ChenPiMain.java:47)的java.util.concurrent.Exchanger.exchange(Exchanger.java:568)处得到nulljava.lang.InterruptedExceptiontjava.lang.Thread.run(Thread.java:748)上面演示了如果一个线程不能等待另一个线程与其交换信息,它将永远等待。其实也可以设置Exchanger等待指定的时间。例如,系统设置玩家兑换装备的匹配时间为60秒,超过该时间将终止交易。packagecom.chenpi;importjava.util.concurrent.Exchanger;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.TimeoutException;/***@Description*@AuthorChenpi*@Date2021/7/11*@Version1.0*/publicclassChenPiMain{publicstaticvoidmain(String[]args){Exchangerexchanger=newExchanger<>();newThread(()->{try{//超时设置为5秒Stringstr=exchanger.exchange("龙剑",5,TimeUnit.SECONDS);System.out.println("交易成功,"+Thread.currentThread().getName()+"Get"+str);}catch(TimeoutExceptione){System.out.println("事务超时!");e.printStackTrace();}catch(InterruptedExceptione)){System.out.println("交易异常终止");e.printStackTrace();}},"周芷若").start();}}//输出结果如下Transactiontimedout!java.util.concurrent.TimeoutException在com.chenpi.ChenPiMain.lambda$main$0(ChenPiMain.java:2)2)在java.lang.Thread.run(Thread.java:748)3Exchanger的应用Exchager在遗传算法和流水线设计等应用中非常有用,比如两个线程之间交换缓冲区,填充缓冲区的线程是在需要时从另一个线程获取一个新清空的缓冲区,并将填充的缓冲区传递给清空缓冲区的线程。packagecom.chenpi;importjava.awt.image.DataBuffer;importjava.util.concurrent.Exchanger;/***@Description*@Author陈皮*@Date2021/7/11*@Version1.0*/publicclassChenPiMain{Exchangerexchanger=newExchanger();DataBufferinitialEmptyBuffer=...一个虚构的类型DataBufferinitialFullBuffer=...classFillingLoopimplementsRunnable{publicvoidrun(){DataBuffercurrentBuffer=initialEmptyBuffer;尝试{while(currentBuffer!=null){addToBuffer(currentBuffer);如果(currentBuffer.isFull()){currentBuffer=exchanger.exchange(currentBuffer);}}}catch(InterruptedExceptionex){...handle...}}}classEmptyingLoopimplementsRunnable{publicvoidrun(){DataBuffercurrentBuffer=initialFullBuffer;尝试{while(currentBuffer!=null){takeFromBuffer(currentBuffer);如果(当前tBuffer.isEmpty()){currentBuffer=exchanger.exchange(currentBuffer);}}}catch(InterruptedExceptionex){...handle...}}}voidstart(){newThread(newFillingLoop()).start();新线程(新EmptyingLoop())。开始();}}点击关注,第一时间了解华为云新技术