Двусторонний обмен между потоками
Общая переменная передает состояние, но не описывает протокол: кто пишет, кто ждет, где конец и что делать при переполнении. Для обмена сообщениями лучше явно задать два направления двумя BlockingQueue.
record Request(long id, String payload) {}
record Response(long id, String payload) {}
BlockingQueue<Request> requests = new ArrayBlockingQueue<>(100);
BlockingQueue<Response> responses = new ArrayBlockingQueue<>(100);
Thread server = new Thread(() -> {
try {
while (!Thread.currentThread().isInterrupted()) {
Request request = requests.take();
responses.put(handle(request));
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
server.start();
requests.put(new Request(1, "ping"));
Response response = responses.take();Ограниченная очередь дает backpressure: put() ждет свободное место. Для запроса-ответа в реальной системе полезно сопоставлять сообщения по id, вводить deadline и передавать сигнал завершения.
Другие варианты:
SynchronousQueue— передача из рук в руки без буфера;Exchanger<T>— парный обмен значениями в одной точке;CompletableFuture<Response>внутри запроса — адресный ответ;PipedInputStream/PipedOutputStream— поток байтов, но сложнее отмена и диагностика.
Атомарные переменные подходят для простого последнего состояния, но не заменяют очередь сообщений.
На собеседовании
- Зачем две очереди? Они явно моделируют два независимых направления.
- Почему очередь лучше общего
List? Она атомарно координирует ожидание и видимость. - Что дает bounded queue? Ограничение памяти и обратное давление.
- Как завершить потребителя? Interrupt, закрываемый канал или специальное terminal-сообщение по согласованному протоколу.