一、简介 Exchanger是自jdk1.5起开始提供的工具套件,一般用于两个工作线程之间交换数据。在本文中我将采取由浅入深的方式来介绍分析这个工具类。首先我们来看看官方的api文档中的叙述:
在以上的描述中,有几个要点:
- 此类提供对外的操作是同步的;
- 用于成对出现的线程之间交换数据;
- 可以视作双向的同步队列;
- 可应用于基因算法、流水线设计等场景。
接着看api文档,这个类提供对外的接口非常简洁,一个无参构造函数,两个重载的范型exchange方法:public V exchange(V x) throws InterruptedExceptionpublic V exchange(V x,long timeout,TimeUnit unit) throws InterruptedException,TimeoutException 从官方的javadoc可以知道,当一个线程到达exchange调用点时,如果它的伙伴线程此前已经调用了此方法,那么它的伙伴会被调度唤醒并与之进行对象交换,然后各自返回。如果它的伙伴还没到达交换点,那么当前线程将会被挂起,直至伙伴线程到达——完成交换正常返回;或者当前线程被中断——抛出中断异常;又或者是等候超时——抛出超时异常。二、一个简单的例子按照某大师的观点,行为知之先,在知道了Exchanger的大致用途并参阅了使用说明后,我们马上动手写个例子来跑一跑:
-
@Title: ExchangerTest
-
@Description: Test class for Exchanger
-
@Company: CSAIR
-
@Author: lixuanbin
-
@Creation: 2014年12月14日
-
@Version:1.0
<span style="color: #0000ff;">public <span style="color: #0000ff;">class<span style="color: #000000;"> ExchangerTest {
<span style="color: #0000ff;">protected <span style="color: #0000ff;">static <span style="color: #0000ff;">final Logger log = Logger.getLogger(ExchangerTest.<span style="color: #0000ff;">class<span style="color: #000000;">);
<span style="color: #0000ff;">private <span style="color: #0000ff;">static <span style="color: #0000ff;">volatile <span style="color: #0000ff;">boolean isDone = <span style="color: #0000ff;">false<span style="color: #000000;">;
<span style="color: #0000ff;">static <span style="color: #0000ff;">class ExchangerProducer <span style="color: #0000ff;">implements<span style="color: #000000;"> Runnable {
<span style="color: #0000ff;">private Exchanger
<span style="color: #0000ff;">private <span style="color: #0000ff;">static <span style="color: #0000ff;">int data = 1<span style="color: #000000;">;
ExchangerProducer(Exchanger
<span style="color: #0000ff;">this.exchanger =<span style="color: #000000;"> exchanger;
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {
</span><span style="color: #0000ff;">while</span> (!Thread.interrupted() && !<span style="color: #000000;">isDone) {
</span><span style="color: #0000ff;">for</span> (<span style="color: #0000ff;">int</span> i = 1; i <= 3; i++<span style="color: #000000;">) {
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
TimeUnit.SECONDS.sleep(</span>1<span style="color: #000000;">);
data </span>=<span style="color: #000000;"> i;
Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.out.println(</span>"producer before: " +<span style="color: #000000;"> data);
data </span>=<span style="color: #000000;"> exchanger.exchange(data);
Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.out.println(</span>"producer after: " +<span style="color: #000000;"> data);
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
log.error(e,e);
}
}
isDone </span>= <span style="color: #0000ff;">true</span><span style="color: #000000;">;
}
}
}
<span style="color: #0000ff;">static <span style="color: #0000ff;">class ExchangerConsumer <span style="color: #0000ff;">implements<span style="color: #000000;"> Runnable {
<span style="color: #0000ff;">private Exchanger
<span style="color: #0000ff;">private <span style="color: #0000ff;">static <span style="color: #0000ff;">int data = 0<span style="color: #000000;">;
ExchangerConsumer(Exchanger
<span style="color: #0000ff;">this.exchanger =<span style="color: #000000;"> exchanger;
}
@Override
</span><span style="color: #0000ff;">public</span> <span style="color: #0000ff;">void</span><span style="color: #000000;"> run() {
</span><span style="color: #0000ff;">while</span> (!Thread.interrupted() && !<span style="color: #000000;">isDone) {
data </span>= 0<span style="color: #000000;">;
Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.out.println(</span>"consumer before : " +<span style="color: #000000;"> data);
</span><span style="color: #0000ff;">try</span><span style="color: #000000;"> {
TimeUnit.SECONDS.sleep(</span>1<span style="color: #000000;">);
data </span>=<span style="color: #000000;"> exchanger.exchange(data);
} </span><span style="color: #0000ff;">catch</span><span style="color: #000000;"> (InterruptedException e) {
log.error(e,e);
}
Sy<a href="https://www.jb51.cc/tag/stem/" target="_blank" class="keywords">stem</a>.out.println(</span>"consumer after : " +<span style="color: #000000;"> data);
}
}
}
<span style="color: #008000;">/**<span style="color: #008000;">
- <span style="color: #808080;">@param<span style="color: #008000;"> args
<span style="color: #008000;">*/
<span style="color: #0000ff;">public <span style="color: #0000ff;">static <span style="color: #0000ff;">void<span style="color: #000000;"> main(String[] args) {
ExecutorService exec =<span style="color: #000000;"> Executors.newCachedThreadPool();
Exchangerexchanger = <span style="color: #0000ff;">new Exchanger <span style="color: #000000;">();
ExchangerProducer producer = <span style="color: #0000ff;">new<span style="color: #000000;"> ExchangerProducer(exchanger);
ExchangerConsumer consumer = <span style="color: #0000ff;">new<span style="color: #000000;"> ExchangerConsumer(exchanger);
exec.execute(producer);
exec.execute(consumer);
exec.shutdown();
<span style="color: #0000ff;">try<span style="color: #000000;"> {
exec.awaitTermination(30<span style="color: #000000;">,TimeUnit.SECONDS);
} <span style="color: #0000ff;">catch<span style="color: #000000;"> (InterruptedException e) {
log.error(e,e);
}
}
}
这大致可以看作是一个简易的生产者消费者模型,有两个任务类,一个递增地产生整数,一个产生整数0,然后双方进行交易。每次交易前的生产者和每次交易后的消费者都会sleep 1秒来模拟数据处理的消耗,并在交易前后把整数值打印到控制台以便检测结果。在这个例子里交易循环只执行三次,采用一个volatile boolean来控制交易双方线程的退出。 我们来看看程序的输出:
<div class="quote_div">consumer before : 0producer before: 1consumer after : 1producer after: 0consumer before : 0producer before: 2producer after: 0consumer after : 2consumer before : 0producer before: 3producer after: 0consumer after : 3