Exchanger in Java

Use `Exchanger` in Java when two threads need a rendezvous point to swap data without broader shared-state coordination.

10.3.3.4 Exchanger

Introduction to Exchanger

The Exchanger class in Java’s java.util.concurrent package provides a synchronization point at which threads can pair and swap elements within pairs. This utility is particularly useful in scenarios where two threads need to exchange data, such as in a producer-consumer model. The Exchanger is designed to facilitate data exchange between two threads, allowing them to swap objects in a thread-safe manner.

How Exchanger Works

The Exchanger works by providing a rendezvous point for two threads. When a thread reaches the exchange() method, it waits for another thread to arrive at the same point. Once both threads have reached the exchange() method, they swap the objects they have, and each thread receives the object provided by the other.

Key Characteristics

  • Synchronization Point: Ensures that two threads meet at a common point to exchange data.
  • Blocking Operation: The exchange() method blocks until another thread arrives.
  • Thread Safety: Manages synchronization internally, ensuring safe data exchange.

Typical Use Cases

  1. Producer-Consumer Scenarios: Where a producer thread generates data that needs to be consumed by a consumer thread.
  2. Pipeline Processing: Where data is processed in stages, and each stage is handled by a different thread.
  3. Double Buffering: Where two threads alternate between reading and writing to shared buffers.

Example: Producer-Consumer Scenario

Let’s explore a practical example where two threads, a producer and a consumer, use an Exchanger to swap data.

 1import java.util.concurrent.Exchanger;
 2
 3public class ExchangerExample {
 4
 5    public static void main(String[] args) {
 6        Exchanger<String> exchanger = new Exchanger<>();
 7
 8        Thread producer = new Thread(new Producer(exchanger));
 9        Thread consumer = new Thread(new Consumer(exchanger));
10
11        producer.start();
12        consumer.start();
13    }
14}
15
16class Producer implements Runnable {
17    private Exchanger<String> exchanger;
18    private String data;
19
20    public Producer(Exchanger<String> exchanger) {
21        this.exchanger = exchanger;
22        this.data = "Data from Producer";
23    }
24
25    @Override
26    public void run() {
27        try {
28            System.out.println("Producer is producing data...");
29            // Simulate data production
30            Thread.sleep(1000);
31            // Exchange data with consumer
32            data = exchanger.exchange(data);
33            System.out.println("Producer received: " + data);
34        } catch (InterruptedException e) {
35            Thread.currentThread().interrupt();
36        }
37    }
38}
39
40class Consumer implements Runnable {
41    private Exchanger<String> exchanger;
42    private String data;
43
44    public Consumer(Exchanger<String> exchanger) {
45        this.exchanger = exchanger;
46        this.data = "Data from Consumer";
47    }
48
49    @Override
50    public void run() {
51        try {
52            System.out.println("Consumer is ready to consume data...");
53            // Simulate data consumption
54            Thread.sleep(1000);
55            // Exchange data with producer
56            data = exchanger.exchange(data);
57            System.out.println("Consumer received: " + data);
58        } catch (InterruptedException e) {
59            Thread.currentThread().interrupt();
60        }
61    }
62}

Explanation

  • Producer Thread: Generates data and waits to exchange it with the consumer.
  • Consumer Thread: Prepares to consume data and waits to exchange it with the producer.
  • Exchanger: Facilitates the data swap between the producer and consumer.

Potential Issues and Considerations

Deadlocks

A potential issue with Exchanger is the risk of deadlocks if one party does not arrive at the exchange point. This can occur if one thread is delayed or encounters an exception before reaching the exchange() method.

Solution: Implement timeouts using the overloaded exchange() method that accepts a timeout parameter.

1data = exchanger.exchange(data, 2, TimeUnit.SECONDS);

Thread Interruption

Threads waiting at the exchange() method can be interrupted, which will throw an InterruptedException. Ensure proper handling of this exception to maintain thread responsiveness.

Best Practices

  • Timeouts: Use timeouts to prevent indefinite blocking.
  • Exception Handling: Handle InterruptedException to ensure graceful shutdown.
  • Resource Management: Ensure that resources are released if a thread is interrupted or a timeout occurs.

Advanced Use Cases

Double Buffering

In double buffering, two threads alternate between reading and writing to shared buffers. The Exchanger can be used to swap the roles of the buffers, ensuring efficient data processing.

 1class DoubleBufferingExample {
 2    private Exchanger<Buffer> exchanger = new Exchanger<>();
 3    private Buffer currentBuffer = new Buffer();
 4
 5    public void process() {
 6        try {
 7            Buffer newBuffer = exchanger.exchange(currentBuffer);
 8            // Process data in newBuffer
 9        } catch (InterruptedException e) {
10            Thread.currentThread().interrupt();
11        }
12    }
13}

Conclusion

The Exchanger class is a powerful tool for synchronizing data exchange between threads in Java. By understanding its operation and potential pitfalls, developers can effectively implement it in various concurrency scenarios. Remember to handle exceptions and consider timeouts to avoid deadlocks and ensure robust applications.

Further Reading

Test Your Knowledge: Java Exchanger and Synchronization Quiz

Loading quiz…

Revised on Thursday, April 23, 2026