Thread Safety Using CyclicBarrier
A synchronization aid that 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.
A situation not uncommon in concurrent programming occurs when a set of two or more threads must wait at a predetermined execution point until all threads in the set have reached that point. To handle such a situation, the concurrent API supplies the CyclicBarrier class. It enables you to define a synchronization object that suspends until the specified number of threads has reached the barrier point.
How it works
Here is the general procedure that you will follow to use CyclicBarrier. First, create a CyclicBarrier object, specifying the number of threads that you will be waiting for. Next, when each thread reaches the barrier, have it call await( ) on that object. This will pause the execution of the thread until all of the other threads also call await( ). Once the specified number of threads has reached the barrier, await( ) will return and execution will resume. Also, if you have specified an action, then that thread is executed.
The await( ) method has the following two forms:
int await( ) throws InterruptedException, BrokenBarrierException
int await(long wait, TimeUnit tu) throws InterruptedException, BrokenBarrierException, TimeoutException
The first form waits until all the threads have reached the barrier point. The second form waits only for the period of time specified by wait. The units represented by wait are specified by tu. Both forms return a value that indicates the order that the threads arrive at the barrier point. The first thread returns a value equal to the number of threads waited upon minus one. The last thread returns zero.
Here is an example that illustrates CyclicBarrier.
package concurency;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author - LeeN
* PROJECT NAME: Final Project IZ0-819
* CREATED ON: Monday 13 September 2021 - 6:10 AM
*/
public class UsingCyclicBarrier {
public void cleanZoo(){
System.out.println("Cleaning zoo");
}
public void addingLions(){
System.out.println("Adding Lions");
}
public void removingLions(){
System.out.println("Removing Lions");
}
public void performTasks(CyclicBarrier c1){
try {
cleanZoo();
c1.await();
addingLions();
c1.await();
removingLions();
} catch (Exception e){
System.out.println( e);
}
}
public static void main(String[] args) {
ExecutorService service = null;
UsingCyclicBarrier zoo = new UsingCyclicBarrier();
try {
service = Executors.newFixedThreadPool(10);
var c1 = new CyclicBarrier(4);
var c2 = new CyclicBarrier(10,()-> System.out.println("finished cleaning zoo"));
for (int i=0; i<4; i++)
service.submit(()-> zoo.performTasks(c1) );
}finally {
if (service != null) service.shutdown();
}
}
}
A CyclicBarrier can be reused because it will release waiting threads each time the specified number of threads calls await( )
As the preceding example shows, the CyclicBarrier offers a streamlined solution to what was previously a complicated problem.