java.util.concurrent.Exchanger提供了一个在阻塞点用于线程成对交换数据的同步机制。举个简单的例子,两个人一起干浇水的活,用两个水盆,A用盆1盛水,B用盆2浇水。
1、A盛满水,等待B将盆2的水用完后和B交换水盆,然后A继续盛水,B继续浇水。
2、B将盆2的水用光,则等待A盛满水后交换水盆,然后A继续盛水,B继续浇水。
3、在发生交换前两者各自干自己的活,互不干扰,只在交换点阻塞。
下面看代码实现。
package test.concurrent;
import java.util.Arrays;
import java.util.concurrent.Exchanger;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.Phaser;
public class TestExchanger {
public static void main(String[] args) throws InterruptedException {
final Exchanger<LinkedBlockingQueue<String>> exchanger = new Exchanger<LinkedBlockingQueue<String>>();
final Phaser phaser = new Phaser(2) {
protected boolean onAdvance(int phaser, int registeredParties) {
return phaser >=2 || 0 == registeredParties;
}
};
Thread filling = new Thread("filling thread" ) {
public void run() {
LinkedBlockingQueue<String> produce = intialEmpty();
while (!phaser.isTerminated()) {
// wait N parties to arrive, and call onAdvance.
System.out.println(Thread.currentThread()+" arrive, phase-"+phaser.getPhase());
phaser.arriveAndAwaitAdvance();
if (produce.isEmpty()) {
//fill
fill(produce);
}
try {
// wait another thread to exchange objects.
produce = exchanger.exchange(produce);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
Thread empting = new Thread("empting thread" ) {
public void run() {
LinkedBlockingQueue<String> consume = initalFull();
while (!phaser.isTerminated()) {
// wait N parties to arrive, and call onAdvance.
System.out.println(Thread.currentThread()+" arrive, phase-"+phaser.getPhase());
phaser.arriveAndAwaitAdvance();
while (!consume.isEmpty()) {
//consume
System.out.print(consume.poll()+"\t");
}
System.out.println();
try {
// wait another thread to exchange objects.
consume = exchanger.exchange(consume);
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
filling.start();
empting.start();
}
private static LinkedBlockingQueue<String> intialEmpty() {
return new LinkedBlockingQueue<String>();
}
private static LinkedBlockingQueue<String> initalFull() {
LinkedBlockingQueue<String> ret = new LinkedBlockingQueue<String>();
fill(ret);
return ret;
}
private static void fill(LinkedBlockingQueue<String> queue) {
String[] values = {"just", "some", "waters"};
try {
queue.addAll(Arrays.asList(values));
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果如下:
Thread[filling thread,5,main] arrive, phase-0
Thread[empting thread,5,main] arrive, phase-0
just some waters
Thread[filling thread,5,main] arrive, phase-1
Thread[empting thread,5,main] arrive, phase-1
just some waters
Thread[filling thread,5,main] arrive, phase-2
Thread[empting thread,5,main] arrive, phase-2
just some waters
Exchanger比较简单,只有一个关键方法exchange,用于阻塞并等待另一个线程同样的调用同一个Exchanger实例的exchange方法,该方法的参数代表想要交换给对方的对象,返回值为对方线程想交换给对方的对象。成对使用,常见场景就是以上代码实现提到的交换存储容器。
另外,以上代码中使用了Phaser工具类,该类是一个多阶段可变参与者协同器,可以看做CyclicBarrier的升级版,包含phase, parties, arrive几个计数控制变量,通过register/deregister改变参与者数量parties,arrive改变参与着到达集合点的数量,phase代表循环数,可通过覆盖onAdvance方法控制是否中断phase继续,需通过phaser.isTerminated()做判断。