Using CyclicBarrier in Java with example

General life example to teach Cyclic Barrier in Java
Here are general life and real time examples on Cyclic barrier. These examples help you understand Cyclic barrier in the easy way.
Sometimes, you might want to threads to wait to get some other task done (run another thread) and then resume those threads after the task is completed. To achieve this, we use a Cyclic barrier.

Consider a generalized example, when we go to a temple, we can see multiple queues of people who bought a 50/- and 100/- rupee ticket. When a VIP comes, both of the queues are paused at a point, after the VIP's darshan is over, both the queues are resumed. Consider, here, both the queues as 2 threads, and the VIP is another thread for which the other two threads has to wait.
Note: Queue refers to a line of people (for example, Follow the 'Q ') but not in computer science terms.
And, as you know any thread can be resumed first due to asynchronous behavior of threads, in generalized terms, even the 50/- rupee 'Q' line can resume first. Before looking into the program, let us look at few important methods.

await(); This method is called on the CyclicBarrier object in the thread which is to be paused. If there are 2 threads that must pause for a cyclic barrier thread to execute, then await(); must be called in the run() of those two threads. If there are two threads that must wait for the barrier to execute, first time this method is called, the thread, in which it is called, will pause, second time it is called, the second thread is paused and the barrier thread is executed. After the barrier thread completes its execution, the two threads in which the await() is called are resumed. There is no guarantee that which will be resumed first.

await(long time,TimeUnit units); This method is same as the above method but the thread here is paused for a given time. If other threads that need to be paused are not paused within the given time, then this thread throws a TimeoutException. You can chose to continue the execution of this thread by handling the exception.

reset(); This method resets the barrier to the initial state. It throws a BrokenBarrierException in the threads that are paused. You can chose to continue those threads' execution by handling the exception. As the threads are resumed, barrier thread will not be executed until again, the required threads are paused.

Note that a paused thread corresponds to the thread waiting at the barrier.

Here is an example, made in accordance with the above said generalized example, optimized for understandability.

import java.util.concurrent.*;
class CyclicBarrierDemo
{
    public static void main(String args[])
    {
        // No.of threads to wait
        // In general terms, no.of queues to wait
        // here 2
        CyclicBarrier cb=new CyclicBarrier(2,new GoVIP());
           
        // Create two threads,
        // a 50/- line and a 100/- line
        new FiftyClass(cb);
        new HundredClass(cb);
    }
}
class FiftyClass extends Thread
{
CyclicBarrier cb;
    public FiftyClass(CyclicBarrier cb)
    {
        // Know that FiftyCLass has to wait, so is the cyclic barrier
        // This object is used to call the await()
        this.cb=cb;
       
        // Start the thread
        // In general terms, let the 50/- queue move
        start();
    }
   
    public void run()
    {
        // They are just 2.5 km away from the
        // god
        System.out.println("2.5 Km to go");
       
        // An InterruptedException is raised.
        // In general, sometimes the situation goes
        // uncontrollable due to heavy rush
        try
        {
            cb.await();
        }catch(Exception e){
            // Then, say, we can't wait
            System.out.println("We can't wait ");
        }
       
        // They completed their task!!
        System.out.println("Hurray! We saw our GOD! +50");
    }
}
class HundredClass extends Thread
{
CyclicBarrier cb;

    public HundredClass(CyclicBarrier cb)
    {
        // Take the CyclicBarrier object
        this.cb=cb;
       
        // Let the 100/- queue move
        start();
    }
   
    public void run()
    {
        // They are just 2 km to go, because
        // they paid a double amount!
        System.out.println("2 Km to go");
        try
        {
            // Slow down, guys! Let the VIP go!
            cb.await();
        }catch(Exception e){
            System.out.println("We can't wait");
        }
       
        // Great, you got it for 100!
        System.out.println("Hurray! We saw our GOD! +100");
    }
   
}
class GoVIP implements Runnable
{
    public void run()
    {
        // See
        System.out.println("Seeing");
       
        // Don't forget to say thanks to those
        // 50/- and 100/- queue people!! :)
        System.out.println("Thank you all");
    }
}

Output of the program


2 Km to go
2.5 Km to go
Seeing
Thank you all
Hurray! We saw our GOD! +50
Hurray! We saw our GOD! +100

Working with reset()

I don't want to re-write the entire example. Consider calling reset() method in the HundredClass run() method before await() is called. Here is the snippet.

        try
        {
            cb.reset();
            // Slow down, guys! Let the VIP go!
            cb.await();
        }catch(Exception e){
            System.out.println("We can't wait");
        }

When the reset() method is called, a BrokenBarrierException is raised in the FiftyClass' run(). So, FiftyClass comes out of waiting state. They, simply say, we can't wait and go on seeing the god. Here is the output.

2.5 Km to go
2 Km to go
We can't wait
Hurray! We saw our GOD! +50

There will be lock because, for the barrier thread to execute (in general terms, for the VIP to go), both FiftyClass and HundredClass must wait. But, when the reset() method is called, the FiftyClass stops waiting. So it goes. Next, await() is called in the HundredClass, therefore HundredClass waits. Therefore, here only one thread is waiting at the barrier, but two threads must wait for VIP thread to execute. As,a result, the HundredClass won't go and the VIP won't go. The FiftyClass happily comes out.

Note: The above situation happens, when the FiftyClass' await() is called before the HundredClass' reset() is called. However, if reset() is called before FiftyClass' await() then things go fine. Due to asynchronous behavior of threads, either HundredClass' reset() or FiftyClass' await() can be called first.

Working with await(long,TimeUnit)

It is simple. Just replace await() with await(long,TimeUnit) in the FiftyClass
try
{
cb.await(2,java.util.concurrent.TimeUnit.NANOSECONDS);
}catch(Exception e){
// Then, say, we can't wait
System.out.println("We can't wait ");
}
Here if other threads doesn't pause in 2 NANOSECONDS, then a TimeoutException is thrown. Here I chose to continue, the FiftyClass to see the god. You may halt too using return; When the FiftyClass people doesn't want to wait for longer time, this is used.

Some real time examples of CyclicBarrier

In real time, a Cyclic Barrier can be used in download managers. Modern download managers, as you know, download the data in packets i.e. they split a file into parts (say 8 parts) and then download each part individually giving each part to a separate thread (i.e. 8 parts, 8 threads). Once after all the threads finish their work (i.e. all the parts are downloaded), the parts are combined. In this process, any part can be downloaded first. Consider, here, that a Cyclic barrier is created with 8 parties (threads), so await() is called in all the 8 threads. And that the barrier thread's job is to combine those downloaded parts. The thread that completes downloading its part, calls the await() method and when the last await() method is called (i.e. the last thread to complete the download calls the await() method), then the barrier thread is executed thereby combining all the downloaded parts into a single file. Here, as you can see, the barrier thread cannot execute until all the threads call await() method because, it to combine the parts, they must be downloaded first.
A simple thing to remember here, is that there is possibly no work, that 8 download threads can do after they reached the barrier. Because, they completed downloading their parts and after the barrier thread is executed, the parts are combined. The downloading is completed. While, I couldn't guarantee that this is what the modern download managers do, I could say that this too can be used.

If you think, i helped you in understanding Cyclic barrier, please share the post.

No comments: