Introduction to Java Exchanger
Java Exchanger class is a synchronization point where threads can swap elements. In other words, Exchanger provides two way mechanism to exchange objects between threads. So now you can give your object and take his objects between pairs. It is some sort of bidirectional form of SynchronousQueue.
It is available from Java 1.5 however it is not very much known class to many Java developer.
Benefits of Java Exchanger
Passing data between threads in java is possible through Exchanger class. It solves the purpose of the problem of bidirectional exchange of objects.
Real Time Example / Use case
In Juice Factory, the juice is first extracted from fruits and stored in a large container, which is being sent for individual Packaging. Considering the above real time example / use case, JuiceExtraction system and Packaging System are the individual independent threads which can pair to exchange the Juice Container. Here the exchange item is the container. During pairing, JuiceExtraction container will pass filled-up container and receive the empty container.
Here is the code sample for the above real time use case:
public class JuiceFactory {
Exchanger<JuiceFactory.Container> exchanger = new Exchanger<JuiceFactory.Container>();
class JuiceExtraction implements Runnable{
Container currentContainer;
public JuiceExtraction(Container c){
this.currentContainer = c;
}
@Override
public void run() {
try {
while(currentContainer != null) {
// Extract juice and fill inside container
int result = currentContainer.fill(20);
if(result > 0) {
// Container capacity is full, pass this container to Packaging
// system through Exchanger.
// wait till get a free container from Packaging system
currentContainer = exchanger.exchange(currentContainer);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Packaging implements Runnable{
Container currentContainer;
public Packaging(Container c){
this.currentContainer = c;
}
@Override
public void run() {
try {
while(currentContainer != null) {
// Takeout juice for packaging in bottle
int result = currentContainer.takeOut(25);
if(result > 0) {
// Container is empty. Pass this empty container to Juice extraction
// system and take another full container for packaging
currentContainer = exchanger.exchange(currentContainer);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Container {
int capacity = 100;
int currentFillup = 0;
public Container (int initialVolume) {
currentFillup = initialVolume;
}
public int fill(int volume) {
if(currentFillup+volume > capacity) {
volume = volume - (capacity - currentFillup);
currentFillup = capacity;
return volume;
} else {
currentFillup+=volume;
return 0;
}
}
public int takeOut(int volume) {
if(currentFillup - volume < 0) {
volume = volume - (volume - currentFillup);
currentFillup = 0;
return volume ;
} else {
currentFillup-=volume;
return 0;
}
}
}
void start() {
new Thread (new JuiceExtraction(new Container(0))).start();
new Thread (new Packaging(new Container(100))).start();
}
}
Other Ideas:
Through Exchanger, we can also address Producer Consumer Problem Statement besides the areas where we need to exchange the objects between threads
If there is an application where it is required to be both one directional exchange and bi-directional based on certain criteria, we can use Exchanger as a one directional exchange (only give and receive null) and also bi-directional (both give and take).