В программировании нередко возникают такие ситуации, когда два или больше потока должны находиться в режиме ожидания в предопределенной точке исполнения до тех пор, пока все эти потоки не достигнут данной точки.
Для этой цели в параллельном API предоставляется класс CyclicBarrier. Он позволяет определить объект синхронизации, который приостанавливается до тех пор, пока определенное количество потоков исполнения не достигнет некоторой барьерной точки.
В классе CyclicBarrier определены следующие конструкторы:
1 2 |
CyclicBarrier(int количество_потоков) CyclicBarrier(int количество_потоков, Runnable действие) |
где параметр количество_потоков определяет число потоков, которые должны достигнуть некоторого барьера до того, как их исполнение будет продолжено.
Во второй форме конструктора параметр действие определяет поток, который будет исполняться по достижении барьера.
Общая процедура применения класса CyclicBarrier следующая. Прежде всего нужно создать объект класса CyclicBarrier, указав количество ожидающих потоков исполнения.
А когда каждый поток исполнения достигнет барьера, следует вызвать метод await() для данного объекта. В итоге исполнение потока будет приостановлено до тех пор, пока метод await() не будет вызван во всех остальных потоках исполнения.
Как только указанное количество потоков исполнения достигнет барьера, произойдет возврат из метода await(), и выполнение будет возобновлено. А если дополнительно указать какое-нибудь действие, то будет выполнен соответствующий поток.
У метода await() имеются следующие общие формы:
1 2 3 |
int await() throws InterruptedException, BrokenBarrierException int await(long ожидание, TimeUnit единица_времени) throws InterruptedException, BrokenBarrierException, TimeoutException |
В первой форме ожидание длится до тех пор, пока каждый поток исполнения не достигнет барьерной точки.
А во второй форме ожидание длится только в течение определенного периода времени, определяемого параметром ожидание. Время ожидания указывается в единицах, обозначаемых параметром единица_времени.
В обеих формах возвращается значение, указывающее порядок, в котором потоки исполнения будут достигать барьерной точки. Первый поток исполнения возвращает значение, равное количеству ожидаемых потоков минус 1, а последний поток возвращает нулевое значение.
Пользуюсь случаем хочу спросить аудиторию про один плагин WordPress. Дело в том, что желаю делать рассылку с помощью плагина CodeCanyon Mailster. Есть ли у вас опыт работы с ним и стоит ли игра свеч?
В приведенном ниже примере программы демонстрируется применение класса CyclicBarrier. Эта программа ожидает до тех пор, пока все три потока достигнут барьерной точки. Как только это произойдет, будет выполнен поток, определяемый действием типа BarAction.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
// Продемонстрировать применение класса CyclicBarrier import java.util.concurrent.*; class BarDemo { public static void main(String args[]) { CyclicBarrier cb = new CyclicBarrier(3, new BarAction()); System.out.println("Запуск потоков"); new MyThread(cb, "A"); new MyThread(cb, "B"); new MyThread(cb, "C"); } } // Поток исполнения, использующий барьер типа CyclicBarrier class MyThread implements Runnable { CyclicBarrier cbar; String name; MyThread(CyclicBarrier c, String n) { cbar = c; name = n; new Thread(this).start(); } public void run() { System.out.println(name); try { cbar.await(); } catch (BrokenBarrierException exc) { System.out.println(exc); } catch (InterruptedException exc) { System.out.println(exc); } } } // Объект этого класса вызывается по достижении // барьера типа CyclicBarrier class BarAction implements Runnable { public void run() { System.out.println("Барьер достигнут"); } } |
Ниже приведен примерный результат выполнения данной программы. ( Конкретный порядок следования потоков исполнения может быть иным. )
1 2 3 4 5 |
Запуск потоков A B C Барьер достигнут |
Класс CyclicBarrier можно использовать повторно, поскольку он освобождает ожидающие потоки исполнения всякий раз, когда метод await() вызывается из заданного количества потоков исполнения. Так, если внести следующие изменения в метод main() из предыдущего примера программы:
1 2 3 4 5 6 7 8 9 10 11 12 |
public static void main(String args[]) { CyclicBarrier cb = new CyclicBarrier(3, new BarAction()); System.out.println("Запуск потоков"); new MyThread(cb, "A"); new MyThread(cb, "B"); new MyThread(cb, "C"); new MyThread(cb, "X"); new MyThread(cb, "Y"); new MyThread(cb, "Z"); } |
то результат ее выполнения будет таким, как показано ниже. ( Конкретный порядок следования потоков исполнения может быть иным. )
1 2 3 4 5 6 7 8 9 |
Запуск потоков A B C Барьер достигунт X Y Z Барьер достигунт |
Как показывает рассмотренный выше пример, класс CyclicBarrier предоставляет изящное решение задачи, которая раньше считалась сложной.