Обмін між потоками. Клас Exchanger, Phaser
Обмін між потоками. Клас Exchanger/Phaser – уроки java
Для взаємодії між потоками у Java є спеціальні класи з пакету `java.util.concurrent`, такі як `Exchanger` і `Phaser`, які полегшують процес комунікації між потоками та допомагають у синхронізації.
Клас Exchanger
`Exchanger` дозволяє двом потокам обмінюватися даними в строго визначеній точці синхронізації. Кожен потік викликає метод `exchange()`, передаючи дані для обміну. Після цього обидва потоки блокуються, поки один з них не передасть свої дані іншому.
Основні моменти:
- Два потоки: Клас працює виключно для двох потоків, які зустрічаються для обміну даними.
- Блокування: Обидва потоки блокуються, поки не викличуть метод `exchange()`. Якщо один потік викликає цей метод, він блокується до тих пір, поки інший не викличе його також.
- Обмін даними: Після того, як обидва потоки викликали `exchange()`, вони обмінюються своїми даними.
Приклад використання Exchanger:
import java.util.concurrent.Exchanger;
public class Main {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
// Перший потік
Thread thread1 = new Thread(() -> {
try {
String data = “Дані від потоку 1”;
System.out.println(“Потік 1 відправляє: ” + data);
String receivedData = exchanger.exchange(data);
System.out.println(“Потік 1 отримав: ” + receivedData);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// Другий потік
Thread thread2 = new Thread(() -> {
try {
String data = “Дані від потоку 2”;
System.out.println(“Потік 2 відправляє: ” + data);
String receivedData = exchanger.exchange(data);
System.out.println(“Потік 2 отримав: ” + receivedData);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
thread1.start();
thread2.start();
}
}
Виведення:
Потік 1 відправляє: Дані від потоку 1
Потік 2 відправляє: Дані від потоку 2
Потік 1 отримав: Дані від потоку 2
Потік 2 отримав: Дані від потоку 1
У цьому прикладі два потоки обмінюються повідомленнями через об’єкт `Exchanger`. Обидва потоки викликають метод `exchange()` і чекають, поки інший потік викличе його також, після чого відбувається обмін.
Клас Phaser
`Phaser` є більш гнучкою версією `CyclicBarrier` та `CountDownLatch` і дозволяє синхронізувати виконання декількох етапів (фаз) для групи потоків. Потоки можуть виконувати роботу в декілька фаз, і `Phaser` забезпечує, що всі учасники завершили поточну фазу, перш ніж перейти до наступної.
Основні моменти:
- Фази: Потоки виконують певну роботу в кілька фаз. Після завершення однієї фази, Phaser не дозволяє переходити до наступної, поки всі потоки не завершать поточну.
- Динамічне додавання/видалення учасників: Клас дозволяє додавати або видаляти учасників (потоки) в процесі виконання.
- Метод `arriveAndAwaitAdvance()`: Потік повідомляє про завершення фази та чекає, поки всі інші учасники також завершать поточну фазу.
Приклад використання Phaser:
import java.util.concurrent.Phaser;
public class Main {
public static void main(String[] args) {
Phaser phaser = new Phaser(1); // Реєструємо “головний” потік
// Створюємо три потоки
for (int i = 1; i <= 3; i++) {
int threadNum = i;
phaser.register(); // Реєструємо кожен потік в фазері
new Thread(() -> {
System.out.println(“Потік ” + threadNum + ” починає першу фазу.”);
phaser.arriveAndAwaitAdvance(); // Завершує першу фазу і чекає інших
System.out.println(“Потік ” + threadNum + ” починає другу фазу.”);
phaser.arriveAndAwaitAdvance(); // Завершує другу фазу і чекає інших
}).start();
}
// Головний потік знімає свою реєстрацію і починає фазування
phaser.arriveAndDeregister();
// Очікування завершення всіх фаз
while (!phaser.isTerminated()) {
// Головний потік може робити щось інше
}
System.out.println(“Всі потоки завершили свої фази.”);
}
}
Виведення:
Потік 1 починає першу фазу.
Потік 2 починає першу фазу.
Потік 3 починає першу фазу.
Потік 1 починає другу фазу.
Потік 2 починає другу фазу.
Потік 3 починає другу фазу.
Всі потоки завершили свої фази.
У цьому прикладі три потоки синхронізуються у двох фазах. Кожен потік починає фазу, виконує деяку роботу, а потім викликає `arriveAndAwaitAdvance()`, щоб чекати, поки інші потоки завершать поточну фазу.
Основні методи класу Phaser:
- `arrive()`: Потік повідомляє, що завершив фазу, але не чекає інших потоків.
- `arriveAndAwaitAdvance()`: Потік повідомляє, що завершив фазу, і чекає, поки всі інші потоки завершать поточну фазу.
- `arriveAndDeregister()`: Потік повідомляє, що завершив фазу та більше не бере участь у фазах.
- `isTerminated()`: Перевіряє, чи завершені всі фази.
Висновок
- Exchanger дозволяє двом потокам обмінюватися даними в точно визначеній точці синхронізації. Це корисно для випадків, коли потоки залежать від результатів один одного.
- Phaser є інструментом для синхронізації багатоступеневих (фазових) завдань між кількома потоками. Це корисно для задач, що виконуються в кілька етапів, коли потоки повинні синхронізуватися після кожного етапу.