Что такое Exchanger и для чего он используется?
Exchanger<V> — это синхронизатор из java.util.concurrent, предназначенный для обмена данными между ровно двумя потоками. Каждый поток вызывает exchange(), передавая свой объект, и блокируется до тех пор, пока второй поток не вызовет тот же метод. После этого каждый получает объект, переданный другим.
Код: простой обмен между двумя потоками
Exchanger<String> exchanger = new Exchanger<>();
Thread producer = new Thread(() -> {
try {
String data = "Данные от производителя";
String received = exchanger.exchange(data); // Блокируется до обмена
System.out.println("Производитель получил: " + received);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread consumer = new Thread(() -> {
try {
String data = "Подтверждение от потребителя";
String received = exchanger.exchange(data);
System.out.println("Потребитель получил: " + received);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
Практический пример — двойная буферизация:
Код: producer-consumer с обменом буферами
Exchanger<List<String>> exchanger = new Exchanger<>();
// Производитель заполняет буфер и обменивается пустым
Thread producer = new Thread(() -> {
List<String> buffer = new ArrayList<>();
try {
while (true) {
buffer.add(generateData());
if (buffer.size() >= 100) {
buffer = exchanger.exchange(buffer); // Отдаёт полный, получает пустой
buffer.clear();
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
// Потребитель обрабатывает полный буфер и возвращает пустой
Thread consumer = new Thread(() -> {
List<String> buffer = new ArrayList<>();
try {
while (true) {
buffer = exchanger.exchange(buffer); // Отдаёт пустой, получает полный
processData(buffer);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Метод с таймаутом:
Пример
String received = exchanger.exchange(data, 5, TimeUnit.SECONDS);
// TimeoutException, если второй поток не вызвал exchange() за 5 секунд
Ключевые особенности:
- Работает строго между двумя потоками. Третий поток будет ждать четвёртого.
- Обмен атомарен — оба потока гарантированно получат объекты друг друга.
- Используйте версию с таймаутом в production, чтобы избежать вечной блокировки.
Частые ошибки:
- Использование с >2 потоками — непредсказуемое поведение.
- Отсутствие обработки
InterruptedException—exchange()прерываема. - Забытый таймаут — вечная блокировка при отсутствии партнёра.
Аналогия:
Exchanger— это точка обмена шпионов на мосту. Каждая сторона (поток) приводит своего человека (данные) и ждёт на мосту, пока другая сторона не придёт со своим. Обмен происходит одновременно.
На собеседовании
Exchanger— это нишевый инструмент. Упомяните его применение в двойной буферизации и pipeline-обработке. В большинстве новых проектов заменяется наBlockingQueueилиCompletableFuture.