Managing Concurrent Processes

public class CyclicBarrierExample {
    private void removeVisitors() {
        System.out.println("Removing visitors");
    }

    private void cleanPlayground() {
        System.out.println("Cleaning the playground");
    }

    private void addVisitors() {
        System.out.println("Adding visitors");
    }

    private void performTask(CyclicBarrier c1, CyclicBarrier c2) {
        try {
            removeVisitors();
            c1.await();
            cleanPlayground();
            c2.await();
            addVisitors();
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        ExecutorService service = null;
        try {
            service = Executors.newFixedThreadPool(3);
            CyclicBarrierExample cyclicBarrierExample = new CyclicBarrierExample();
            CyclicBarrier c1 = new CyclicBarrier(3);;
            CyclicBarrier c2 = new CyclicBarrier(3, () -> System.out.println("*** Playground cleaned!"));
            for (int i = 0; i < 3; i++) {
                service.submit(() -> cyclicBarrierExample.performTask(c1, c2));
            }
        } finally {
            if (service != null) {
                service.shutdown();
            }
        }
    }
}

In this example performTask() method was updated to use CyclicBarrier objects. Like synchronizing on the same object, coordinating a task with a CyclicBarrier requires the object to be static or passed to the thread performing the task. The output will be the following :

Removing visitors
Removing visitors
Removing visitors
Cleaning the playground
Cleaning the playground
Cleaning the playground
*** Playground cleaned!
Adding visitors
Adding visitors
Adding visitors

All of the results are now organized. Note that two different constructors were used for CyclicBarrier objects, the latter of which called a Runnable method upon completion.

When using thread pool with cyclic barrier, it is essential for the number of available threads to be at least as large as CyclicBarrier limit value. Or at least the number of threads submitted to executor service must be as large as CyclicBarrier limit value. Otherwise the barrier would never be reached as threads available in the pool would be stuck waiting for the barrier to be complete. This is a form of deadlock.

There is a slight loss in performance to be expected from using a CyclicBarrier. If at least one of the threads takes much time to complete the step, others have to wait.

One instance of CyclicBarrier can be used more than once. For instance, in the example below c1 could be used twice, latterly instead of c2, since they deal with the same number of threads.

Previous Next

Leave a Reply

Your email address will not be published. Required fields are marked *