An Exchanger<T> is a synchronization point where exactly two threads swap objects: each calls exchange(myItem), blocks until the partner arrives, and then each receives the other's item. It is a two-party rendezvous with a payload — the only synchronizer designed for paired data hand-off rather than counting.
How it works
exchange(x) blocks until another thread also calls exchange(y) on the same Exchanger. At that instant both calls return: the first thread gets y, the second gets x. There is a timed overload, exchange(x, timeout, unit), that throws TimeoutException if no partner shows up. It is strictly two threads at a time — if three arrive, they pair off in some order; an exchanger does not coordinate more than a pair per swap.
Exchanger<List<String>> exchanger = new Exchanger<>();
// Producer thread: fills a buffer, swaps it for an empty one
Runnable producer = () -> {
List<String> buffer = new ArrayList<>();
try {
while (true) {
fill(buffer);
buffer = exchanger.exchange(buffer); // get the empty one back
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
// Consumer thread: drains a buffer, swaps it back empty
Runnable consumer = () -> {
List<String> buffer = new ArrayList<>();
try {
while (true) {
buffer = exchanger.exchange(buffer); // get the full one
drain(buffer); // then return it emptied
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
The canonical use case
The textbook pattern is double buffering in a producer/consumer pipeline: the producer fills buffer A while the consumer drains buffer B, then they exchange so the producer gets the now-empty B and the consumer gets the full A. No shared queue, no copying — the buffers are simply traded. This can reduce allocation pressure compared to a BlockingQueue because the same two buffers are recycled.