CyclicBarrier
This Article is part of Series of Articles on Java 8 Concurrency Tutorial.
In this article, we’ll focus on a the concept of CyclicBarrier in the Java language.
CyclicBarrier
CyclicBarrier allows a set of threads to all wait for each other to reach a common barrier point. CyclicBarriers are useful in programs involving a fixed sized party of threads that must occasionally wait for each other. The barrier is called cyclic because it can be re-used after the waiting threads are released.
CyclicBarrier are Similar to CountDownLatch but CyclicBarrier provide some additional features like
Reseting CyclicBarrier & Supports an optional Runnable command that is run once per barrier point.
CyclicBarrier Example
Key Points
CyclicBarrier(int parties, Runnable barrierAction) :
Creates a new CyclicBarrier that will trip when the given number of parties (threads) are waiting upon it, and which will execute the given barrier action when the barrier is tripped, performed by the last thread entering the barrier.
getNumberWaiting()
Returns the number of parties currently waiting at the barrier.
Синхронизаторы пакета concurrent
Объекты синхронизации Synchroniser из пакета java.util.concurrent включают :
Semaphore | объект синхронизации, ограничивающий количество потоков, которые могут «войти» в заданный участок кода; |
CountDownLatch | объект синхронизации, разрешающий вход в заданный участок кода при выполнении определенных условий; |
CyclicBarrier | объект синхронизации типа «барьер», блокирующий выполнение определенного кода для заданного количества потоков; |
Exchanger | объект синхронизации, позволяющий провести обмен данными между двумя потоками; |
Phaser | объект синхронизации типа «барьер», но в отличие от CyclicBarrier, предоставляет больше гибкости. |
Объект синхронизации Semaphore
Как было отмечено выше, Semaphore ограничивает доступ к определенному участку кода, иначе говоря, к общему ресурсу, в качестве которого могут выступать программые/аппаратные ресурсы или файловая система.
Для управления доступом к общему ресурсу Semaphore использует счетчик. Если значение счетчика больше нуля, то поток исполнения получает разрешение, после чего значение счетчика семафора уменьшается на единицу. При значении счетчика равным нулю очередному потоку исполнения в доступе будет отказано, и он будет заблокирован до тех пор, пока не будут освобождены ресурсы.
Как только один из потоков исполнения освободит ресурсы, т.е. завершит исполнение определенного участка кода, то значение счетчика семафора увеличивается на единицу. Если в это время имеется ожидающий разрешения другой поток исполнения, то он сразу же его получает.
Конструкторы Semaphore
Класс Semaphore имеет два приведенных ниже конструктора :
Параметр permits определяет исходное значение счетчика разрешений, т.е. количество потоков исполнения, которым может быть одновременно предоставлен доступ к общему ресурсу. По умолчанию ожидающим потокам предоставляется разрешение в неопределенном порядке. Если же использовать второй конструктор и параметру справедливости fair присвоить значение true, то разрешения будут предоставляться ожидающим потокам исполнения в том порядке, в каком они его запрашивали.
Метод получения разрешения acquire
Чтобы получить у семафора разрешение необходимо вызвать у него один из перегруженных методов acquire :
Первый метод без параметра запрашивает одно разрешение, а второй в качестве параметра использует количество разрешений. Обычно используется первый метод. Если разрешение не будет получено, то исполнение вызывающего потока будет приостановлено до тех пор, пока не будет получено разрешение, т.е. поток блокируется.
Освобождение ресурса
Чтобы освободить разрешение у семафора следует вызвать у него один из перегруженных методов release :
В первом методе освобождается одно разрешение, а во втором — количество разрешений, обозначенное параметром number.
Пример использования Semaphore
В примере несколько всадников с лошадьми должны пройти контроль перед скачками. Количество контроллеров меньше количества всадников, поэтому некоторые всадники будут дожидаться, пока не освободиться один из контроллеров.
Общий ресурс CONTROL_PLACES, символизирующий контролеров и используемый всеми потоками, выделен оператором synchronized. С помощью семафора осуществляется контроль доступа только одному потоку.
Результат выполнения примера с семафором
Обратите внимание, что «всадник 3» завершил проверку, после чего «всадник 6» вошел в блокируемый участок кода. А вот «всадник 7» успел раньше вывести сообщение о входе в блокируемый участок кода, чем «всадник 1» сообщил о его «покидании».
Объект синхронизации CountDownLatch
Объект синхронизации потоков CountDownLatch представляет собой «защелку с обратным отсчетом» : несколько потоков, выполняя определенный код, блокируются до тех пор, пока не будут выполнены заданные условия. Количество условий определяются счетчиком. Как только счетчик обнулится, т.е. будут выполнены все условия, самоблокировки выполняемых потоков снимаются, и они продолжают выполнение кода.
Таким образом, CountDownLatch также, как и Semaphore, работает со счетчиком, обнуление которого снимает самоблокировки выполняемых потоков. Конструктор CountDownLatch :
Параметр number определяет количество событий, которые должны произойти до того момента, когда будет снята самоблокировка.
Метод самоблокировки await
CountDownLatch имеет два перегруженных метода await для самоблокировки :
В первом методе ожидание длится до тех пор, пока счетчик CountDownLatch не достигнет нуля. А во втором методе ожидание длится только в течение определенного периода времени, определяемого параметром ожидание wait. Время ожидания указывается в единицах unit объекта перечисления TimeUnit, определяюший временно́е разбиение. Существуют следующие значения данного перечисления :
- DAYS
- HOURS
- MINUTES
- SECONDS
- MICROSECONDS
- MILLISECONDS
- NANOSECONDS
Метод уменьшения счетчика countDown
Чтобы уменьшить счетчик объекта CountDownLatch следует вызвать метод countDown :
Примером CountDownLatch может служить паром, собирающий определенное количество транспорта и пассажиров, или экскурсовод, собирающий группу из заданного количества туристов.
Пример использования CountDownLatch
В примере несколько всадников должны подъехать к барьеру. Как только все всадники выстроятся перед барьером, будут даны команды «На старт», «Внимание», «Марш». После этого барьер опустится и начнутся скачки. Объект синхронизации CountDownLatch выполняет роль счетчика количества всадников и команд.
При «выходе на старт» поток вызывает методы countDown, уменьшая значение счетчика на 1, и await, блокируя самого себя в ожидании обнуления счетчика «защелки». Как только все потоки выстроятся на «старте» с интервалом в 1 сек. подаются команды. Каждая команда сопровождается уменьшением счетчика. После обнуления счетчика «защелки» потоки продолжают выполнение дальнейшего кода.
Результат выполнения примера
Объект синхронизации CyclicBarrier
Объект синхронизации CyclicBarrier представляет собой барьерную синхронизацию, используемую, как правило, в распределённых вычислениях. Особенно эффективно использование барьеров при циклических расчетах. При барьерной синхронизации алгоритм расчета делят на несколько потоков. С помощью барьера организуют точку сбора частичных результатов вычислений, в которой подводится итог этапа вычислений.
В исходном коде барьер для группы потоков означает, что каждый поток должен остановиться в определенном месте и ожидать прихода остальных потоков группы. Как только все потоки достигнут барьера, их выполнение продолжится.
Класс CyclicBarrier имеет 2 конструктора :
В первом конструкторе задается количество потоков, которые должны достигнуть барьера, чтобы после этого одновременно продолжить выполнение кода. Во втором конструкторе дополнительно задается реализующий интерфейс Runnable класс, который должен быть запущен после прихода к барьеру всех потоков. Поток запускать самостоятельно НЕ НУЖНО. CyclicBarrier это делает автоматически.
Для указания потоку о достижении барьера нужно вызвать один из перегруженных методов await :
Назначение параметров wait и unit у второго метода описано выше (см. CountDownLatch).
Циклический барьер CyclicBarrier похож на CountDownLatch. Главное различие между ними связано с тем, что «защелку» нельзя использовать повторно после того, как её счётчик обнулится, а барьер можно использовать (в цикле). С точки зрения API циклический барьер CyclicBarrier имеет только метод самоблокировки await и не имеет метода декрементации счетчика, а также позволяет подключить и автоматически запускать дополнительный потоковый класс при достижении барьера всех исполняемых потоков.
Пример использования CyclicBarrier
В примере организуется переправа. Паром может вместить только 3 автомобиля. Количество автомобилей 9. Роль парома выполняет объект синхронизации FerryBarrier, которому в качестве второго параметра передается реализующий интерфейс Runnable класс FerryBoat. Как только 3 потока достигнут барьера автоматически будет запущен FerryBoat, после завершения работы которого потоки продолжают свою работу.
Обратите внимание, что потоки подходят к барьеру с интервалом в 400 ms. Время задержки у барьера/переправы (после того, как собралось необходимое количество потоков), составляет 500 ms, если не считать время вывода сообщений в консоль. За это время к барьеру успевает подойти еще один поток. Что мы и видим при выводе сообщений в консоль.
Результат выполнения примера
Варьируя временем на переправе и временем прихода автомобилей на переправу, можно либо заставить паром простаивать, либо будут простаивать автомобили на переправе.
Объект синхронизации Exchanger
Класс Exchanger (обменник) предназначен для упрощения процесса обмена данными между двумя потоками исполнения. Принцип действия класса Exchanger связан с ожиданием того, что два отдельных потока должны вызвать его метод exchange. Как только это произойдет, Exchanger произведет обмен данными, предоставляемыми обоими потоками.
Обменник является обобщенным классом, он параметризируется типом объекта передачи :
Необходимо отметить, что обменник поддерживает передачу NULL значения, что дает возможность использовать его для передачи объекта в одну сторону или места синхронизации двух потоков.
Exchanger содержит перегруженный метод exchange, имеющий следующие формы :
Параметр buffer является ссылкой на обмениваемые данные. Метод возвращает данные из другого потока исполнения. Вторая форма метода позволяет определить время ожидания. Параметры wait и unit описаны выше. Метод exchange, вызванный в одном потоке, не завершится успешно до тех пор, пока он не будет вызван из второго потока исполнения.
Пример использования Exchanger
В примере использования объекта синхронизации Exchanger два почтальона из пунктов А и Б отправляются в соседние поселки В и Г доставить письма. Каждый из почтальонов должен доставить по письму в каждый из поселков. Чтобы не делать лишний круг, они встречаются в промежуточном поселке Д и обмениваются одним письмом. В результате этого каждому из почтальонов придется доставить письма только в один поселок. В примере все «шаги» почтальонов фиксируются выводом соответствующих сообщений в консоль.
Результат выполнения примера
В консоль будет выведена следующая информация :
Объект синхронизации Phaser
Phaser (фазировщик), как и CyclicBarrier, является реализацией объекта синхронизации типа «Барьер» (CyclicBarrier). В отличии от CyclicBarrier, Phaser предоставляет больше гибкости. Чтобы лучше понять Phaser, можно привести два наглядно демонстрирующих его использование примера.
В качестве первого примера можно рассмотреть несколько потоков исполнения, реализующих процесс обработки заказов из трех стадий. На первой стадии отдельные потоки исполнения проверяют сведения о клиенте, наличие товара на складе и их стоимость. На второй стадии вычисляется стоимость заказа и стоимость доставки. На заключительной стадии подтверждается оплата и определяется ориентировочное время доставки. Во втором примере несколько потоков реализуют перевозку пассажиров городским транспортом. Пассажиры ожидают транспорт на разных остановках. Транспорт, останавливаясь на остановках, одних пассажиров «сажает», других «высаживает».
В этих примерах общим является то, что один объект синхронизации Phaser, исполняющий роль заказа и транспорта, играет главную роль, а другие потоки вступают в работу при определенном состоянии Phaser. Таким образом, класс Phaser позволяет определить объект синхронизации, ожидающий завершения определенной фазы. После этого он переходит к следующей фазе и снова ожидает ее завершения.
Важные особенности Phaser :
- Phaser может иметь несколько фаз (барьеров). Если количество фаз равно 1, то плавно переходим к CyclicBarrier (осталось только все исполнительные потоки остановить у барьера).
- Каждая фаза (цикл синхронизации) имеет свой номер.
- Количество участников-потоков для каждой фазы жестко не задано и может меняться. Исполнительный поток может регистрироваться в качестве участника и отменять свое участие;
- Исполнительный поток не обязан ожидать, пока все остальные участники соберутся у барьера. Достаточно только сообщить о своем прибытии.
Для создания объекта Phaser используется один из конструкторов :
Параметр parties определяет количество участников, которые должны пройти все фазы. Первый конструктор создает объект Phaser без каких-либо участников. Второй конструктор регистрирует передаваемое в конструктор количество участников. Третий и четвертый конструкторы дополнительно устанавливают родительский объект Phaser.
При создании экземпляр класса Phaser находится в нулевой фазе. В очередном состоянии (фазе) синхронизатор находится в ожидании до тех пор, пока все зарегистрированные потоки не завершат данную фазу. Потоки извещают об этом, вызывая один из методов arrive() или arriveAndAwaitAdvance().
Методы объекта синхронизации Phaser
Метод | Описание |
---|---|
int register() | Метод регистририрует участника и возвращает номер текущей фазы. |
int arrive() | Метод указывает на завершения выполнения текущей фазы и возвращает номер фазы. Если же работа Phaser закончена, то метод вернет отрицательное число. При вызове метода arrive поток не приостанавливается, а продолжает выполняться. |
int arriveAndAwaitAdvance() | Метод вызывается потоком/участником, чтобы указать, что он завершил текущую фазу. Это аналог метода CyclicBarrier.await(), сообщающего о прибытии к барьеру. |
int arriveAndDeregister() | Метод arriveAndDeregister сообщает о завершении всех фаз участником и снимается с регистрации. Данный метод возвращает номер текущей фазы или отрицательное число, если Phaser завершил свою работу |
int getPhase() | Получение номера текущей фазы. |
Следующее анимационное изображение наглядно демонстрирует работу объекта синхронизации Phaser — участник регистрируется в определенной фазе синхронизатора; при переходе синхронизатора в заданное состояние (фазу) участник снимается с регистрации. Количество участников в разных фазах синхронизатора может отличаться.
Пример использования Phaser
В примере PhaserExample создается несколько потоков, играющих роль пассажиров. Phaser играет роль метро, которое должно проследовать вдоль нескольких станций. Каждая станция (фаза) имеет свой номер. Класс Passenger играет роль пассажира, который на одной из станции должен зайти в вагон, а на другой выйти. Количество пассажиров, а также их места посадки и высадки, формируются случайным образом.
Листинг класса Passenger
Конструктор класса Passenger получает значение идентификатора, номера станций посадки и назначения (высадки). При создании объекта в консоль выводится информация о пассажире (метод toString).
Как только Phaser переходит в определенную фазу, номер которой соответствует станции посадки пассажира, то поток данного Passenger стартует (run) и выводит в консоль сообщение, что пассажир вошел в вагон, т.е. находится в ожидании следующей станции/фазы (arriveAndAwaitAdvance). Если следующая станция/фаза не будет соответствовать станции назначения, то Passenger продолжит свой путь. Как только Phaser перейдет в фазу, номер которой соответствует номеру станции назначания пассажира, то цикл контроля завершится и поток продолжит работу. С задержкой в 500 ms он сообщит, что покинул вагон и отменит регистрацию в Phaser (arriveAndDeregister).
Таким образом, поток/пассажир дожидается свой фазы/станции в цикле, выделенной в коде пунктирными комментариями. Вызов метода arriveAndAwaitAdvance возвращает значение следующего номера фазы, т.е. участник будет вызван при переходе Phaser в новое состояние. Если в этом состоянии значение фазы (getPhase) будет соответствовать номеру destination, то цикл прервется, в противном случае, ожидание следующей фазы и повторное выполнение проверки условия while.
Примечание : Passenger является внутренним классом примера/класса PhaserExample, и для описания вынесен из общего кода, чтобы не загромождать листинг.
Листинг примера PhaserExample
В примере сначала создается объект синхронизации PHASER. После этого формируется массив пассажиров. При создании объекта Passenger случайным образом определяются станция посадки и станция назначения. После того, как массив пассажиров подготовлен, PHASER в цикле начинает менять свое состояние. На каждом шаге выполняется проверка «станции посадки пассажира». Если она соответствует значению фазы, то данный пассажир входит в вагон метро, т.е. регистрируется в PHASER и поток стартует. Таким образом, регистрация участников (исполнительных потоков) выполняется при нахождении PHASER в определенном состоянии/фазе. Пассажир покинет вагон при достижении метро станции назначения, т.е. при нахождении PHASER в соответствующей фазе. Но это произойдет уже в коде класса Passenger, рассмотренного выше.
Barriers in Java concurrency
A barrier in the real world helps to separate two different concepts, for example two countries. In programming, it’s a technique to achieve threads synchronization. Java implements its own version of barriers in java.util.concurrent package.
Your cloud data engineering ebook
Data engineering patterns on the cloud
There are at least 84 common ways to solve data engineering problems with cloud services. Do you know them all?
At the first part of this article we’ll see some theoretical concepts of barriers. We’ll start by defining them and will terminate by describing the Java’s implementation. The second part will present a sample code using barriers.
Barriers in Java
Barrier is considered as a kind of meeting point for several threads. While this point isn’t reached by all concerned threads, program execution won’t continue. So, we can deduce that the barrier is assigned to a group of threads and is treated as reached when all threads from the group arrive to it. When even one thread has the problems (for example: it’s interrupted), the barrier is considered as broken.
In Java, the barriers are represented by java.util.concurrent.CyclicBarrier class. This barrier is cyclic because it can be used for several thread groups. When constructing CyclicBarrier object, we must pass to it the number of threads composing the group. We can also pass an instance of Runnable. It’ll represent the action invoked when the barrier is reached. As we mentioned earlier, this barrier is considered as broken when one of threads fails (timeout, code failure, interruption). BrokenBarrierException or InterruptedException are thrown in this case, depending of the case of failure. InterruptedException will be thrown when the thread is interrupted. BrokenBarrierException’ll be thrown for all other reasons.
6 methods are implemented in CyclicBarrier:
— await with or without timeout: it’s used to signal that the barrier was reached by one of threads from thread group. If the thread reaching the barrier isn’t the last thread from the group, it remains in waiting state until the last one calls await().
All waiting threads awake when the barrier is reached by the last thread or one of threads (also called parts) from the group fails. Another special case of awake is the invocation of reset method by one of the threads.
This method can also take as parameter one value with time unit to mark the timeout of waiting. When specified time elapses, TimeoutException is thrown.
— getNumberWaiting: returns the number of threads actually waiting for the barrier.
— getParties: returns the number of all threads needed to reach the barrier.
— isBroken: returns true if the barrier is in broken state.
— reset: this method resets the barrier to the initial state. All waiting threads throw BrokenBarrierException exception.
As we can see until now, CyclicBarrier seems to behave as CountDownLatch, explained in the article about CountDownLatch in Java. CountDownLatch is executed only once while we can reuse barrier through reset method. Another difference is that we can invoke one Runnable object when all threads reach the barrier. We can’t do it with CountDownLatch. Thirdly, CountDownLatch’s counter is decremented every time when countDown() method is invoked. And it’s not important if one thread calls it 5 times. With CyclicBarrier, it does matter. Only await() calls from separate threads are taken in consideration to decide if the barrier was reached or no.
Example of barriers in Java
As example, we’ll use the illustration of run competition. We precise some runners, each one has its own specification: Runner is a normal runner, SprintRunner is a sprinter (must reach the barrier before 1 seconds timeout), FourLegsRunner is a runner with 4 legs and HoledShoesRunner is a runner who is running with holed shoes. All try to reach the barrier represented by meta variable.
And this is a sample output after executing test cases:
This article described the concept of barriers in concurrency programs. Barrier allows one group of threads to wait until all threads reach the barrier. It’s another way, with synchronized blocks, CountDownLatch, to synchronize execution of several threads. The barrier in Java is represented by CyclicBarrier class that is purely thread-aware. await() method invoked several times in the same thread will be count at exactly one barrier reach.
Your cloud data engineering ebook
Data engineering patterns on the cloud
There are at least 84 common ways to solve data engineering problems with cloud services. Do you know them all?
If you liked it, you should read:
The comments are moderated. I publish them when I answer, so don’t worry if you don’t see yours immediately 🙂
Newsletter Get new posts, recommended reading and other exclusive information every week. SPAM free — no 3rd party ads, only the information about waitingforcode!
CyclicBarrier in Java
30% less RAM and a 30% smaller base image for running a Spring Boot application? Yes, please.
Alpaquita Linux was designed to efficiently run containerized Java applications.
It’s meant to handle heavy workloads and do it well.
And the Alpaquita Containers incorporates Liberica JDK Lite, a Java runtime tailored to cloud-based services:
Repeatedly, code that works in dev breaks down in production. Java performance issues are difficult to track down or predict.
Simply put, Digma provides immediate code feedback. As an IDE plugin, it identifies issues with your code as it is currently running in test and prod.
The feedback is available from the minute you are writing it.
Imagine being alerted to any regression or code smell as you’re running and debugging locally. Also, identifying weak spots that need attending to, based on integration testing results.
Of course, Digma is free for developers.
An interesting read.
As always, the writeup is super practical and based on a simple application that can work with documents with a mix of encrypted and unencrypted fields.
We rely on other people’s code in our own work. Every day.
It might be the language you’re writing in, the framework you’re building on, or some esoteric piece of software that does one thing so well you never found the need to implement it yourself.
The problem is, of course, when things fall apart in production — debugging the implementation of a 3rd party library you have no intimate knowledge of is, to say the least, tricky.
Lightrun is a new kind of debugger.
It’s one geared specifically towards real-life production environments. Using Lightrun, you can drill down into running applications, including 3rd party dependencies, with real-time logs, snapshots, and metrics.
Learn more in this quick, 5-minute Lightrun tutorial:
Slow MySQL query performance is all too common. Of course it is. A good way to go is, naturally, a dedicated profiler that actually understands the ins and outs of MySQL.
The Jet Profiler was built for MySQL only, so it can do things like real-time query performance, focus on most used tables or most frequent queries, quickly identify performance issues and basically help you optimize your queries.
Critically, it has very minimal impact on your server’s performance, with most of the profiling work done separately — so it needs no server changes, agents or separate services.
Basically, you install the desktop application, connect to your MySQL server, hit the record button, and you’ll have results within minutes:
DbSchema is a super-flexible database designer, which can take you from designing the DB with your team all the way to safely deploying the schema.
The way it does all of that is by using a design model, a database-independent image of the schema, which can be shared in a team using GIT and compared or deployed on to any database.
And, of course, it can be heavily visual, allowing you to interact with the database using diagrams, visually compose queries, explore the data, generate random data, import data or build HTML5 database reports.
Get started with Spring 5 and Spring Boot 2, through the Learn Spring course:
1. Introduction
CyclicBarriers are synchronization constructs that were introduced with Java 5 as a part of the java.util.concurrent package.
In this article, we’ll explore this implementation in a concurrency scenario.
2. Java Concurrency – Synchronizers
The java.util.concurrent package contains several classes that help manage a set of threads that collaborate with each other. Some of these include:
- CyclicBarrier
- Phaser
- CountDownLatch
- Exchanger
- Semaphore
- SynchronousQueue
These classes offer out of the box functionality for common interaction patterns between threads.
If we have a set of threads that communicate with each other and resemble one of the common patterns, we can simply reuse the appropriate library classes (also called Synchronizers) instead of trying to come up with a custom scheme using a set of locks and condition objects and the synchronized keyword.
Let’s focus on the CyclicBarrier going forward.
3. CyclicBarrier
A CyclicBarrier is a synchronizer that allows a set of threads to wait for each other to reach a common execution point, also called a barrier.
CyclicBarriers are used in programs in which we have a fixed number of threads that must wait for each other to reach a common point before continuing execution.
The barrier is called cyclic because it can be re-used after the waiting threads are released.
4. Usage
The constructor for a CyclicBarrier is simple. It takes a single integer that denotes the number of threads that need to call the await() method on the barrier instance to signify reaching the common execution point:
The threads that need to synchronize their execution are also called parties and calling the await() method is how we can register that a certain thread has reached the barrier point.
This call is synchronous and the thread calling this method suspends execution till a specified number of threads have called the same method on the barrier. This situation where the required number of threads have called await(), is called tripping the barrier.
Optionally, we can pass the second argument to the constructor, which is a Runnable instance. This has logic that would be run by the last thread that trips the barrier:
5. Implementation
To see CyclicBarrier in action, let’s consider the following scenario:
There’s an operation that a fixed number of threads perform and store the corresponding results in a list. When all threads finish performing their action, one of them (typically the last one that trips the barrier) starts processing the data that was fetched by each of these.
Let’s implement the main class where all the action happens:
This class is pretty straight forward – NUM_WORKERS is the number of threads that are going to execute and NUM_PARTIAL_RESULTS is the number of results that each of the worker threads is going to produce.
Finally, we have partialResults that are a list that’s going to store the results of each of these worker threads. Do note that this list is a SynchronizedList because multiple threads will be writing to it at the same time, and the add() method isn’t thread-safe on a plain ArrayList.
Now let’s implement the logic of each of the worker threads:
We’ll now implement the logic that runs when the barrier has been tripped.
To keep things simple, let’s just add all the numbers in the partial results list:
The final step would be to construct the CyclicBarrier and kick things off with a main() method:
In the above code, we initialized the cyclic barrier with 5 threads that each produce 3 integers as a part of their computation and store the same in the resulting list.
Once the barrier is tripped, the last thread that tripped the barrier executes the logic specified in the AggregatorThread, namely – add all the numbers produced by the threads.
6. Results
Here is the output from one execution of the above program – each execution might create different results as the threads can be spawned in a different order:
As the above output shows, Thread 4 is the one that trips the barrier and also executes the final aggregation logic. It is also not necessary that threads are actually run in the order that they’re started as the above example shows.
7. Conclusion
In this article, we saw what a CyclicBarrier is, and what kind of situations it is helpful in.
We also implemented a scenario where we needed a fixed number of threads to reach a fixed execution point, before continuing with other program logic.
As always, the code for the tutorial can be found over on GitHub.
Slow MySQL query performance is all too common. Of course it is. A good way to go is, naturally, a dedicated profiler that actually understands the ins and outs of MySQL.
The Jet Profiler was built for MySQL only, so it can do things like real-time query performance, focus on most used tables or most frequent queries, quickly identify performance issues and basically help you optimize your queries.
Critically, it has very minimal impact on your server’s performance, with most of the profiling work done separately — so it needs no server changes, agents or separate services.
Basically, you install the desktop application, connect to your MySQL server, hit the record button, and you’ll have results within minutes: