Gymterview
middle

Как поделиться данными между двумя потоками?

Существует несколько способов передачи данных между потоками. Выбор зависит от паттерна взаимодействия.

1. Общий объект с синхронизацией

Самый простой способ – потоки работают с одним и тем же объектом, доступ синхронизирован:

Пример
public class SharedData {
    private String message;

    public synchronized void setMessage(String msg) {
        this.message = msg;
        notify(); // Уведомить ожидающий поток
    }

    public synchronized String getMessage() throws InterruptedException {
        while (message == null) {
            wait(); // Ждать, пока данные не будут записаны
        }
        return message;
    }
}

2. BlockingQueue (производитель-потребитель)

Рекомендуемый способ для паттерна «производитель-потребитель»:

Пример: обмен через BlockingQueue
BlockingQueue<String> queue = new LinkedBlockingQueue<>(100);

// Производитель
Thread producer = new Thread(() -> {
    try {
        queue.put("Сообщение от производителя");
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

// Потребитель
Thread consumer = new Thread(() -> {
    try {
        String msg = queue.take(); // Блокируется, пока данные не появятся
        System.out.println("Получено: " + msg);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

producer.start();
consumer.start();

3. Exchanger (обмен между двумя потоками)

Класс Exchanger<V> предназначен для двустороннего обмена данными между ровно двумя потоками. Каждый поток вызывает exchange(), передаёт свои данные и получает данные другого потока:

Пример: обмен через Exchanger
Exchanger<String> exchanger = new Exchanger<>();

Thread t1 = new Thread(() -> {
    try {
        String fromT2 = exchanger.exchange("Данные от T1");
        System.out.println("T1 получил: " + fromT2);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

Thread t2 = new Thread(() -> {
    try {
        String fromT1 = exchanger.exchange("Данные от T2");
        System.out.println("T2 получил: " + fromT1);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
});

t1.start();
t2.start();

4. Concurrent-коллекции

Потокобезопасные коллекции из java.util.concurrent:

Коллекция Применение
ConcurrentHashMap Конкурентное чтение/запись ключ-значение
CopyOnWriteArrayList Редкая запись, частое чтение
ConcurrentLinkedQueue Неблокирующая очередь

5. Future / CompletableFuture

Для передачи результата из фонового потока в вызывающий:

Пример
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> "Результат вычисления");
String result = future.get(); // Блокирует до получения результата

6. Pipe (PipedInputStream / PipedOutputStream)

Для потоковой передачи данных (редко используется, есть более удобные альтернативы):

Пример
PipedOutputStream out = new PipedOutputStream();
PipedInputStream in = new PipedInputStream(out);
// Один поток пишет в out, другой читает из in

Сводная таблица

Способ Паттерн Блокировка Направление
Общий объект + synchronized Произвольный Да Двустороннее
BlockingQueue Производитель-потребитель Да Однонаправленное
Exchanger Обмен между парой потоков Да Двустороннее
CompletableFuture Асинхронный результат Опционально Однонаправленное
Concurrent-коллекции Конкурентный доступ Частично Двустороннее

Аналогия из жизни. BlockingQueue – это конвейерная лента на фабрике: один рабочий кладёт детали, другой забирает. Exchanger – это рукопожатие с обменом документами: два человека встречаются, каждый отдаёт свой пакет и получает чужой. Future – это квитанция: вы отдаёте задачу и потом приходите за результатом.

На собеседовании. Начните с BlockingQueue – это самый распространённый и безопасный способ для паттерна «производитель-потребитель». Упомяните Exchanger – его редко знают. Покажите, что знаете разницу между блокирующими и неблокирующими подходами. Также отметьте, что передача данных через общий объект без синхронизации – это race condition.