Використання CyclicBarrier у Java
Клас CyclicBarrier
у Java є засобом синхронізації, що дозволяє групі потоків чекати один на одного до досягнення загальної бар’єрної точки. Це корисно в сценаріях, коли потрібно, щоб декілька потоків почали обробку одночасно після досягнення певного стану.
На відміну від CountDownLatch
, CyclicBarrier
можна використовувати повторно після того, як всі очікуючі потоки будуть звільнені, що робить його підходящим для циклічних або ітеративних операцій.
Основні особливості CyclicBarrier
- Повторне використання: Після звільнення всіх очікуючих потоків, бар’єр можна використовувати повторно.
- Необов’язковий Runnable: Можна вказати необов’язкову задачу
Runnable
, яка буде виконана після досягнення бар’єру всіма потоками.
Базове використання CyclicBarrier
Ось простий приклад, що демонструє використання CyclicBarrier
:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
private static final int NUMBER_OF_THREADS = 3;
private static final CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS, new Runnable() {
@Override
public void run() {
System.out.println("Усі учасники досягли бар'єру, починаємо гру");
}
});
public static void main(String[] args) {
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
new Thread(new Task()).start();
}
}
static class Task implements Runnable {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " чекає на бар'єрі.");
barrier.await(); // Очікування на бар'єрі
System.out.println(Thread.currentThread().getName() + " перейшов через бар'єр.");
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
У цьому прикладі:
- Ініціалізація бар’єру:
CyclicBarrier
ініціалізується з кількістю потоків (NUMBER_OF_THREADS
), які повинні чекати на бар’єр, та необов’язковою задачею Runnable, яка виконується після досягнення бар’єру всіма потоками. - Реалізація завдання: Клас Task реалізує
Runnable
і викликаєbarrier.await()
. Кожен потік чекає на бар’єрі, доки всі потоки не досягнуть його. - Виконання потоків: Створюються три потоки, і кожен з них чекає на бар’єрі. Після того, як всі потоки досягли бар’єру, виконується дія бар’єра, і потоки продовжують виконання.
Розширене використання CyclicBarrier
- Обробка зламаного бар’єру: Якщо один з очікуючих потоків переривається або перевищує час очікування,
CyclicBarrier
вважається зламаним. Ви можете перевірити, чи зламаний бар’єр, використовуючиbarrier.isBroken()
та обробити це відповідно. - Час очікування: Можна вказати час очікування для методу
await()
, щоб уникнути нескінченного очікування.barrier.await(5, TimeUnit.SECONDS);
- Скидання бар’єру: Ви можете скинути бар’єр до початкового стану, використовуючи
barrier.reset()
, що може бути корисним, якщо ви хочете використовувати бар’єр на іншій фазі або ітерації.
Приклад з часом очікування та скиданням
Ось приклад, що включає обробку часу очікування та скидання бар’єру:
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class CyclicBarrierAdvancedExample {
private static final int NUMBER_OF_THREADS = 3;
private static final CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS, new Runnable() {
@Override
public void run() {
System.out.println("Усі учасники досягли бар'єру, починаємо гру");
}
});
public static void main(String[] args) {
for (int i = 0; i < NUMBER_OF_THREADS; i++) {
new Thread(new Task()).start();
}
}
static class Task implements Runnable {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " чекає на бар'єрі.");
barrier.await(5, TimeUnit.SECONDS); // Очікування на бар'єрі з тайм-аутом
System.out.println(Thread.currentThread().getName() + " перейшов через бар'єр.");
} catch (InterruptedException | BrokenBarrierException | TimeoutException e) {
e.printStackTrace();
if (barrier.isBroken()) {
System.out.println("Бар'єр зламаний, скидаємо...");
barrier.reset(); // Скидання бар'єру, якщо він зламаний
}
}
}
}
}
У цьому розширеному прикладі:
- Обробка часу очікування: Кожен потік чекає на бар’єрі з часом очікування 5 секунд. Якщо час очікування перевищено, кидається
TimeoutException
. - Перевірка зламаного бар’єру: Якщо бар’єр зламаний, потік виводить повідомлення та скидає бар’єр за допомогою
barrier.reset()
.
Висновок
Клас CyclicBarrier
у Java забезпечує потужний механізм для синхронізації фіксованої кількості потоків на загальній
бар’єрній точці.
Його повторне використання та необов’язкова дія бар’єра роблять його гнучким інструментом для координації потоків
у циклічних або ітеративних завданнях.
Розуміння та використання CyclicBarrier
дозволяє розробникам створювати
надійні багатопотокові додатки, які вимагають синхронізації між кількома потоками.