Gymterview
middle

Что такое 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 потоками — непредсказуемое поведение.
  • Отсутствие обработки InterruptedExceptionexchange() прерываема.
  • Забытый таймаут — вечная блокировка при отсутствии партнёра.

Аналогия: Exchanger — это точка обмена шпионов на мосту. Каждая сторона (поток) приводит своего человека (данные) и ждёт на мосту, пока другая сторона не придёт со своим. Обмен происходит одновременно.

На собеседовании Exchanger — это нишевый инструмент. Упомяните его применение в двойной буферизации и pipeline-обработке. В большинстве новых проектов заменяется на BlockingQueue или CompletableFuture.