Skip to main content
  1. Java Concurrency (java.util.concurrent)/

Exchanger

2 mins

Exchanger is a specialized synchronizer that allows exactly two threads to exchange objects at a rendezvous point. It is useful in pipeline designs and producer-consumer scenarios with symmetrical work.

Source Code #

View Source on GitHub

Mechanism: The “Dual Buffer Handoff” #

Internally, Exchanger uses a “dual buffer” approach. When the first thread arrives, it parks its object in an internal “slot” and waits. When the second thread arrives, it takes the object from the slot, replaces it with its own, and both threads return with the new object.

public V exchange(V x) throws InterruptedException;

If the system has multiple threads performing exchanges simultaneously, Exchanger uses an arena-based approach to reduce contention on the central slot by spreading threads across different exchange areas.

Canonical Usage #

When to use: Use Exchanger when you have exactly two threads that must pass data to each other symmetrically (e.g., a double-buffering algorithm where one thread fills a buffer while the other consumes it, and then they swap).

Common Patterns:

  • Double-Buffering: Producer thread fills a buffer while the consumer processes another. When both are done, they swap buffers through the Exchanger.
  • Bidirectional Information Flow: Two agents in a system need to periodically synchronize and exchange their state for a simulation.
// Exchanger for swapping a data buffer between two threads
Exchanger<DataBuffer> exchanger = new Exchanger<>();

// Thread A: The Producer/Loader
executor.execute(() -> {
    DataBuffer buffer = new DataBuffer();
    while (isRunning) {
        buffer.fill(); // Fill buffer from source
        try {
            buffer = exchanger.exchange(buffer); // Hand over full, get back empty
        } catch (InterruptedException e) { return; }
    }
});

// Thread B: The Consumer/Processor
executor.execute(() -> {
    DataBuffer buffer = new DataBuffer();
    while (isRunning) {
        try {
            buffer = exchanger.exchange(buffer); // Hand over empty, get back full
            buffer.process(); // Process the filled buffer
        } catch (InterruptedException e) { return; }
    }
});

Performance Considerations #

  • Pros: Symmetrical handoff without an intermediate queue. Extremely low memory overhead.
  • Cons: Only works for pairs of threads. If the third thread arrives, it will block until another thread is available for a new exchange pair.