Урок 27: Обмен данными между потоками
Обмен данными между потоками — это важный аспект многопоточного программирования. В Java для этой цели используются различные механизмы, такие как wait()
, notify()
, notifyAll()
, а также высокоуровневые структуры данных, такие как BlockingQueue
. Эти механизмы позволяют потокам безопасно передавать данные и синхронизировать своё выполнение.
Использование wait() и notify()
Методы wait()
, notify()
и notifyAll()
позволяют потокам взаимодействовать друг с другом, ожидая выполнения определённых условий. Рассмотрим пример использования этих методов:
class Data {
private String packet;
// Метод синхронизирован для безопасного доступа из разных потоков
public synchronized void send(String packet) {
this.packet = packet;
notify();
}
public synchronized String receive() throws InterruptedException {
wait();
return packet;
}
}
public class WaitNotifyExample {
public static void main(String[] args) {
Data data = new Data();
Thread sender = new Thread(() -> {
data.send("Привет, мир!");
});
Thread receiver = new Thread(() -> {
try {
String receivedMessage = data.receive();
System.out.println("Получено сообщение: " + receivedMessage);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
receiver.start();
sender.start();
}
}
Результат выполнения
Получено сообщение: Привет, мир!
Использование BlockingQueue
BlockingQueue
— это очередь с блокировкой, которая позволяет потокам безопасно обмениваться данными, автоматически управляя блокировками и уведомлениями. Пример использования BlockingQueue
:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueExample {
public static void main(String[] args) {
BlockingQueue queue = new ArrayBlockingQueue<>(10);
Thread producer = new Thread(() -> {
try {
queue.put("Привет, мир!");
System.out.println("Сообщение отправлено");
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread consumer = new Thread(() -> {
try {
String message = queue.take();
System.out.println("Получено сообщение: " + message);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
}
}
Результат выполнения
Сообщение отправлено
Получено сообщение: Привет, мир!
Упражнения
Упражнение 1: Использование wait() и notify()
Напишите программу, в которой один поток генерирует числа от 1 до 5, а другой поток их выводит. Используйте методы wait()
и notify()
для координации работы потоков.
Решение:
class NumberGenerator {
private int number;
private boolean ready = false;
public synchronized void generate() throws InterruptedException {
for (int i = 1; i <= 5; i++) {
while (ready) {
wait();
}
number = i;
ready = true;
notify();
}
}
public synchronized void print() throws InterruptedException {
for (int i = 1; i <= 5; i++) {
while (!ready) {
wait();
}
System.out.println("Сгенерировано число: " + number);
ready = false;
notify();
}
}
}
public class NumberGeneratorExample {
public static void main(String[] args) {
NumberGenerator generator = new NumberGenerator();
Thread generatorThread = new Thread(() -> {
try {
generator.generate();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread printerThread = new Thread(() -> {
try {
generator.print();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
generatorThread.start();
printerThread.start();
}
}
Объяснение: Программа создает два потока: один генерирует числа от 1 до 5, а другой их выводит. Для координации работы потоков используются методы wait()
и notify()
.
Упражнение 2: Использование BlockingQueue
Напишите программу, которая создает очередь и два потока: один поток добавляет элементы в очередь, а другой извлекает их. Используйте BlockingQueue
для обмена данными между потоками.
Решение:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueExample {
public static void main(String[] args) {
BlockingQueue queue = new ArrayBlockingQueue<>(5);
Thread producer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
queue.put(i);
System.out.println("Добавлено: " + i);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
Thread consumer = new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
int value = queue.take();
System.out.println("Извлечено: " + value);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
producer.start();
consumer.start();
}
}
Объяснение: Программа создает очередь и два потока: один поток добавляет числа в очередь, а другой их извлекает. Для обмена данными между потоками используется BlockingQueue
, которая автоматически управляет блокировками и уведомлениями.