Класс Phaser, примеры реализации кода в Java

Класс Phaser, примеры реализации кода в JavaВ версии JDК 7 внедрен новый класс синхронизации под названием Phaser. Главное его назначение — синхронизировать потоки исполнения, которые пред­ставляют одну или несколько стадий (или фаз) выполнения действия. Например, в прикладной программе может быть несколько потоков исполнения, реализующих три стадии обработки заказов.

На первой стадии отдельные потоки исполне­ния используются для того, чтобы проверить сведения о клиенте, наличие товара на складе и его цену. По завершении этой стадии остаются два потока исполнения, где на второй стадии вычисляется стоимость доставки и сумма соответствующего налога, а на заключительной стадии подтверждается оплата и определяется ори­ентировочное время доставки.

В прошлом для синхронизации нескольких пото­ков исполнения в такой прикладной программе пришлось бы немало потрудиться. А с появлением класса Phaser этот процесс значительно упростился.

Прежде всего следует иметь в виду, что класс Phaser действует подобно описан­ному ранее классу CyclicBarrier, за исключением того, что он поддерживает не­сколько фаз.

В итоге класс Phaser позволяет определить объект синхронизации, ожидающий завершения определенной фазы. Затем он переходит к следующей фазе и снова ожидает ее завершения. Следует также иметь в виду, что класс Phaser можно использовать и для синхронизации только одной фазы. В этом отношении он действует подобно классу CyclicBarrier, хотя главное его назначение — син­хронизация нескольких фаз. В классе Phaser определяются четыре конструктора.

В данной статье упоминаются следующие два конструктора этого класса:

Первый конструктор создает синхронизатор фаз с нулевым регистрационным счетом, а второй устанавливает значение регистрационного счета равным задан­ному количеству_сторон. Объекты, регистрируемые синхронизатором фаз, за­частую обозначаются термином сторона.

Обычно имеется полное соответствие количества регистрируемых объектов и синхронизируемых потоков исполне­ния, хотя этого и не требуется. В обоих случаях текущая фаза является нулевой. Поэтому когда создается экземпляр класса Phaser, он первоначально находится в нулевой фазе.

Обычно класс Phaser используется следующим образом. Сначала создается новый экземпляр класса Phaser. Затем синхронизатор фаз регистрирует одну или несколько сторон, вызывая метод register() или указывая нужное количе­ство сторон в конструкторе класса Phaser.

Пользуюсь случаем, хочу сказать что мы желаем изменить шаблон для нашего блога. Нашел интересный сайт htmldownload.com где доступны самые разные HTML шаблоны. Если вам нравится какой-нибудь дизайн вставьте ссылку в комментарии.

Синхронизатор фаз ожидает до тех пор, пока все зарегистрированные стороны не завершат фазу. Сторона извещает об этом, вызывая один из многих методов, предоставляемых классом Phaser, на­пример метод arrive() или arriveAndAwaitAdvance().

Как только все стороны достигнут данной фазы, она считается завершенной, и синхронизатор фаз может перейти к следующей фазе (если она имеется) или завершить свою работу. Далее этот процесс поясняется более подробно.

Класс Phaser, примеры реализации кода в Java

Для регистрации стороны после создания объекта класса Phaser следует вы­звать метод register(). Ниже приведена общая форма этого метода. В итоге он возвратит номер регистрируемой фазы.

Чтобы сообщить о завершении фазы, сторона должна вызвать метод arrive( ) или какой-нибудь его вариант. Когда количество достижений конца фазы сравня­ется с количеством зарегистрированных сторон, фаза завершится и объект класса Phaser перейдет к следующей фазе (если она имеется). Метод arrive() имеет следующую общую форму:

Этот метод сообщает, что сторона (обычно поток исполнения) завершила не­которую задачу (или ее часть) . Он возвращает текущий номер фазы. Если работа синхронизатора фаз завершена, этот метод возвращает отрицательное значение.

Метод arrive() не приостанавливает исполнение вызывающего потока. Это означает, что он не ожидает завершения фазы. Этот метод должен быть вызван толь­ко зарегистрированной стороной.

Если требуется указать завершение фазы, а затем ожидать завершения этой фазы всеми остальными зарегистрированными сторонами, следует вызвать метод arriveAndAwaitAdvance(). Ниже приведена общая форма этого метода.

Этот метод ожидает до тех пор, пока все стороны не достигнут данной фазы, а затем возвращает номер следующей фазы или отрицательное значение, если синхронизатор фаз завершил свою работу. Метод ariveAndAwaitAdvance() дол­жен быть вызван только зарегистрированной стороной.

Поток исполнения может достигнуть данной фазы, а затем сняться с регистрации, вызвав метод arriveAndDeregister(). Ниже приведена общая форма этого метода.

Этот метод возвращает номер текущей фазы или отрицательное значение,если синхронизатор фаз завершил свою работу. Он не ожидает завершения фазы. Метод arriveAndDeregister() должен быть вызван только зарегистрированной стороной.

Чтобы получить номер текущей фазы, следует вызвать метод getPhase(). Его общая форма выглядит следующим образом:

Когда создается объект класса Phaser, первая фаза получает нулевой номер, вторая фаза — номер 1, третья фаза — номер 2 и т.д. Если вызывающий объект класса Phaser завершил свою работу, возвращается отрицательное значение.

В приведенном ниже примере программы демонстрируется применение класса Phaser. В этой программе создаются три потока, каждый из которых имеет три фазы своего исполнения. Для синхронизации каждой фазы применяется класс Phaser.

Ниже приведен результат, выводимый данной программой.

 

Рассмотрим подробнее основные части данной программы. Сначала в методе main() создается объект phsr типа Phaser с начальным счетом сторон, равным 1 (что соответствует основному потоку исполнения).

Затем создаются три объекта типа MyThread и запускаются три соответствующих потока исполнения. Обратите внимание на то, что объекту типа MyThread передается ссылка на объект phsr синхронизатор фаз. Объекты типа MyThread используют этот синхронизатор фаз для синхронизации своих действий.

Затем в методе main() вызывается метод getPhase(), чтобы получить номер текущей фазы (который первоначально явля­ется нулевым), а после этого — метод arriveAndAwaitAdvance(). В итоге выпол­нение метода main() приостанавливается до тех пор, пока не завершится нулевая, по существу, первая фаза.

Но этого не произойдет до тех пор, пока все объекты типа MyThread не вызовут метод arriveAndAwaitAdvance(). Как только это про­изойдет, метод main() возобновит свое выполнение, сообщив о завершении нуле­вой фазы, и перейдет к следующей фазе. Этот процесс повторяется до завершения всех трех фаз. Затем в методе main( ) вызывается метод arriveAndDeregister() ,чтобы снять с регистрации все три объекта типа MyThread.

В итоге зарегистриро­ванных сторон больше не остается, а следовательно, перейдя к следующей фазе, синхронизатор фаз завершит свою работу.

Теперь рассмотрим объект типа MyThread. Прежде всего обратите вниманиена то, что конструктору передается ссылка на синхронизатор фаз, в котором новый поток исполнения регистрируется далее как отдельная сторона.

Таким образом, каждый новый объект типа MyThread становится стороной, зарегистрированной синхронизатором фаз, переданным конструктору этого объекта.

Обратите также внимание на то, что у каждого потока исполнения имеются три фазы. В данном примере каждая фаза состоит из метки-заполнителя, которая просто отображает имя потока исполнения и то, что он делает.

Безусловно, реальный код выполнял бы в потоке более полезные действия. Между первыми двумя фазами поток испол­нения вызывает метод arriveAndAwaitAdvance(). Таким образом, каждый по­ток исполнения ожидает завершения фазы всеми остальными потоками, включая и основной поток. По завершении всех потоков исполнения, включая и основной, синхронизатор фаз переходит к следующей фазе.

А по завершении третьей фазы каждый поток исполнения снимается с регистрации, вызывая метод arriveAndDeregister(). Как поясняется в комментариях к объектам типа MyThread, метод sleep() вызывается исключительно в иллюстративных целях, чтобы предотвра­тить нарушение вывода из-за многопоточности, хотя это и не обязательно для пра­вильного функционирования синхронизатора фаз.

Если удалить вызовы метода sleep(), то выводимый результат может выглядеть немного запутанным, но фазы все равно будут синхронизированы правильно.

Следует также иметь в виду, что в рассматриваемом здесь примере используют­ся три однотипных потока исполнения, хотя это и не обязательное требование. Каждая сторона, пользующаяся синхронизатором фаз, может быть совершенно непохожей на остальные, выполняя свою задачу.

Все, что происходит при переходе к следующей фазе, вполне поддается кон­тролю. Для этого следует переопределить метод onAdvance(). Этот метод вызы­вается исполняющей средой, когда синхронизатор фаз переходит от одной фазы к следующей. Ниже приведена общая форма данного метода.

Здесь параметр фаза обозначает текущий номер фазы перед его приращением, а параметр количество_сторон — число зарегистрированных сторон.

Для тогочтобы завершить работу синхронизатора фаз, метод onAdvance() должен возвра­тить логическое значение true. А для того чтобы продолжить работу синхрони­затора фаз, метод onAdvance() должен возвратить булевское значение false.

В версии по умолчанию метод onAdvance() возвращает логическое значение true, чтобы завершить работу синхронизатора фаз, если зарегистрированныхсторон больше нет. Как правило, переопределяемая версия данного метода долж­на следовать этой практике.

Метод onAdvance() может быть, в частности, переопределен для того, что­бы дать синхронизатору фаз возможность выполнить заданное количество фаз, а затем остановиться.

Ниже приведен характерный тому пример. В данном при­мере создается класс MyPhaser, расширяющий класс Phaser таким образом, что­бы выполнять заданное количество фаз. С этой целью переопределяется метод onAdvance().

Конструктор класса MyPhaser принимает один аргумент, задающий количество выполняемых фаз. Обратите внимание на то, что класс MyPhaser ав­томатически регистрирует одну сторону. Это удобно для целей данного примера,но у конкретной прикладной программы могут быть другие потребности.

Ниже приведен результат, выводимый данной программой.

В методе main( ) создается один экземпляр класса Phaser. В качестве аргумен­та ему передается значение 4. Это означает, что он будет выполняться в течениечетырех фаз, а затем завершится. Затем создаются три потока исполнения и на­чинается следующий цикл, как показано ниже.

В этом цикле метод arriveAndAwaitAdvance() вызывается до завершения работы синхронизатора фаз, а этого не произойдет до тех пор, пока не будет вы­полнено определенное количество фаз.

В данном случае цикл будет продолжать­ся до тех пор, пока не завершатся все четыре фазы. Следует также иметь в виду, что метод arriveAndAwaitAdvance() вызывается из потоков исполнения вплоть до завершения работы синхронизатора фаз в данном цикле. Это означает, что по­токи исполняются до завершения заданного количества фаз.

А теперь рассмотрим подробнее исходный код метода onAdvance(). Всякий раз, когда вызывается метод onAdvance(), ему передается текущая фаза и коли­чество зарегистрированных сторон. Если текущая фаза соответствует указанной фазе или количество зарегистрированных сторон равно нулю, метод onAdvance() возвращает логическое значение true, прекращая таким образом работу синхро­низатора фаз.

Это делается в приведенном ниже фрагменте кода. Как можно за­метить, для достижения желаемого результата необходимо совсем немного кода.

Прежде чем завершить эту статью, следует заметить, что расширять класс Phaser совсем не обязательно. Как и в предыдущем примере, для этого достаточно пере­определить метод onAdvance().

В некоторых случаях может быть создан более компактный код с помощью анонимного внутреннего класса, переопределяющего метод onAdvance().

У класса Phaser имеются дополнительные возможности, которые моrут ока­заться полезными в прикладных программах. В частности, вызвав метод awaitAdvance(), можно ожидать конкретной фазы, как показано ниже.

Здесь параметр фаза обозначает номер фазы, в течение которой метод awaitAdvance() находится в состоянии ожидания до тех пор, пока не произойдет пере­ход к следующей фазе.

Этот метод завершится немедленно, если передаваемый ему параметр фаза не будет равен текущей фазе. Он завершится сразу и в том слу­чае, если синхронизатор фаз завершит работу.

Но если в качестве параметра фазаэтому методу будет передана текущая фаза, то он будет ожидать перехода к сле­дующей фазе. Этот метод должен быть вызван только зарегистрированной сто­роной. Имеется также прерывающая версия этого метода под названием awaitAdvanceInterruptibly().

Чтобы зарегистрировать несколько сторон, следует вызвать метод bulkRegister( ), чтобы получить количество зарегистрированных сторон — метод getRegisteredParties(), чтобы получить количество сторон, достигших или не достигших своей фазы — метод getArrivedParties() или getUnarrivedParties() соответственно, а чтобы перевести синхронизатор фаз в завершенное состояние — метод forceTermination().

Класс Phaser позволяет также создать дерево синхронизаторов фаз. Он снаб­жен двумя дополнительными конструкторами, которые позволяют указать роди­тельский синхронизатор фаз и метод getParent().

Интересное видео для наших читателей: