Home > Blogs > CyclicBarriers

CyclicBarriers

By  Aug 19, 2008

Topics: Programming, Java

One of my favorite APIs discussed in Java Concurrency in Practice is the CyclicBarrier. Here is some code that demonstrates its use.

Let's start with some sample code that uses multiple threads to do some processing of some sort. What kind of sort? Who knows, we'll just make it sleep for some random amount of time:

import java.util.Random;

public class Launcher {
    private static final Random RND = new Random(System.nanoTime());
    private static int NUM = 10;
   
    public static void main(String[] args) throws Exception {
        for (int i=0;i
            Thread t = new Thread(new RandomWorker());
            t.start();
        }
        System.out.println("Main complete");
    }
   
    public static class RandomWorker implements Runnable{   
        public void run() {
            try {
                log(" waiting at ");
                log(" starting at ");
                Thread.sleep(RND.nextInt(1500));
                log(" done at ");
                log(" exiting at ");
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        public static void log(String message){
            System.out.println(Thread.currentThread().getName() +
                    message + System.nanoTime() );
        }
    }
}
This code is fairly straightforward. Start a bunch of threads, where each thread waits for a random time before exiting, and logs things along the way. Here is some sample output:

Thread-0 waiting at 1219192716846503000
Thread-0 starting at 1219192716846852000
Thread-1 waiting at 1219192716847250000
Thread-1 starting at 1219192716847307000
Thread-2 waiting at 1219192716848897000
Thread-2 starting at 1219192716849021000
Thread-3 waiting at 1219192716849268000
Thread-3 starting at 1219192716849323000
Thread-4 waiting at 1219192716853280000
Thread-4 starting at 1219192716853390000
Thread-5 waiting at 1219192716853664000
Thread-5 starting at 1219192716853719000
Thread-6 waiting at 1219192716853988000
Thread-6 starting at 1219192716854042000
Thread-7 waiting at 1219192716854292000
Thread-7 starting at 1219192716854346000
Thread-8 waiting at 1219192716854595000
Thread-8 starting at 1219192716854649000
Main complete
Thread-9 waiting at 1219192716855010000
Thread-9 starting at 1219192716855064000
Thread-3 done at 1219192717051514000
Thread-3 exiting at 1219192717051692000
Thread-8 done at 1219192717135802000
Thread-8 exiting at 1219192717135969000
Thread-0 done at 1219192717142098000
Thread-0 exiting at 1219192717142263000
Thread-2 done at 1219192717403398000
Thread-2 exiting at 1219192717403523000
Thread-9 done at 1219192717464220000
Thread-9 exiting at 1219192717464386000
Thread-5 done at 1219192717856847000
Thread-5 exiting at 1219192717856953000
Thread-7 done at 1219192718076498000
Thread-7 exiting at 1219192718076661000
Thread-1 done at 1219192718186434000
Thread-1 exiting at 1219192718186711000
Thread-6 done at 1219192718216194000
Thread-6 exiting at 1219192718216347000
Thread-4 done at 1219192718231518000
Thread-4 exiting at 1219192718231665000

About what we would expect, but what a mess! The threads are in all kinds of different states along the way, everything intermingled. What if you wanted to bring some order to this chaos, and keep the threads in uniform state? Pre-Java 5.0 here is how it could be done:

public class Launcher2 {
    private static final Random RND = new Random(System.nanoTime());
    private static int NUM = 10;
    private static int CNT = NUM;
    private static int CNT2 = NUM;
    private static int CNT3 = NUM + 1;
    private static Object lock = new Object();

    public static void main(String[] args) throws Exception {
        for (int i=0;i
            Thread t = new Thread(new RandomWorker());
            t.start();
        }

        int cnt = 0;
        synchronized(lock){
            cnt = --CNT3;
        }
        while (cnt > 0){
            synchronized(lock){
                cnt = CNT3;
            }
        }       
        System.out.println("Main complete");
    }
   
    public static class RandomWorker implements Runnable{   
        public void run() {
            try {
                log(" waiting at ");

                int cnt = 0;
                synchronized(lock){
                    cnt = --CNT;
                }
                while (cnt > 0){
                    synchronized(lock){
                        cnt = CNT;
                    }
                }
                log(" starting at ");
                Thread.sleep(RND.nextInt(1500));
                log(" done at ");

                synchronized(lock){
                    cnt = --CNT2;
                }
                while (cnt > 0){
                    synchronized(lock){
                        cnt = CNT2;
                    }
                }
                log(" exiting at ");
                synchronized(lock){
                    cnt = --CNT3;
                }
                while (cnt > 0){
                    synchronized(lock){
                        cnt = CNT3;
                    }
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        public static void log(String message){
            System.out.println(Thread.currentThread().getName() +
                    message + System.nanoTime() );
        }
    }

}
This will work, give it a try. It sure is ugly though. Here is the same thing using our new friend that Java Concurrency in Practice introduced us to:

import java.util.Random;
import java.util.concurrent.CyclicBarrier;

public class Launcher {
    private static final Random RND = new Random(System.nanoTime());
    private static int NUM = 10;
    private static final CyclicBarrier GATE = new CyclicBarrier(NUM);
    private static final CyclicBarrier MAIN_GATE = new CyclicBarrier(NUM+1);
   
    public static void main(String[] args) throws Exception {
        for (int i=0;i
            Thread t = new Thread(new RandomWorker());
            t.start();
        }
        MAIN_GATE.await();
        System.out.println("Main complete");
    }
   
    public static class RandomWorker implements Runnable{   
        public void run() {
            try {
                log(" waiting at ");
                GATE.await();
                log(" starting at ");
                Thread.sleep(RND.nextInt(1500));
                log(" done at ");
                GATE.await();
                log(" exiting at ");
                MAIN_GATE.await();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        public static void log(String message){
            System.out.println(Thread.currentThread().getName() +
                    message + System.nanoTime() );
        }
    }
}
Much nicer! And indeed we get much more organized output:

Thread-0 waiting at 1219194254146484000
Thread-2 waiting at 1219194254147249000
Thread-1 waiting at 1219194254146642000
Thread-3 waiting at 1219194254147679000
Thread-4 waiting at 1219194254149004000
Thread-5 waiting at 1219194254149309000
Thread-6 waiting at 1219194254149622000
Thread-7 waiting at 1219194254149999000
Thread-8 waiting at 1219194254150268000
Thread-9 waiting at 1219194254150458000
Thread-9 starting at 1219194254158258000
Thread-0 starting at 1219194254159083000
Thread-2 starting at 1219194254160662000
Thread-8 starting at 1219194254161207000
Thread-7 starting at 1219194254161164000
Thread-6 starting at 1219194254161126000
Thread-5 starting at 1219194254161089000
Thread-4 starting at 1219194254161051000
Thread-3 starting at 1219194254160990000
Thread-1 starting at 1219194254160877000
Thread-4 done at 1219194254251401000
Thread-5 done at 1219194254472933000
Thread-8 done at 1219194254689718000
Thread-2 done at 1219194254707353000
Thread-3 done at 1219194254872402000
Thread-7 done at 1219194255050874000
Thread-1 done at 1219194255184146000
Thread-0 done at 1219194255276631000
Thread-6 done at 1219194255512319000
Thread-9 done at 1219194255584594000
Thread-9 exiting at 1219194255585049000
Thread-4 exiting at 1219194255585236000
Thread-5 exiting at 1219194255586937000
Thread-8 exiting at 1219194255588666000
Thread-2 exiting at 1219194255588801000
Thread-3 exiting at 1219194255588923000
Thread-7 exiting at 1219194255589077000
Thread-1 exiting at 1219194255589186000
Thread-6 exiting at 1219194255589353000
Thread-0 exiting at 1219194255589286000
Main complete

Become an InformIT Member

Take advantage of special member promotions, everyday discounts, quick access to saved content, and more! Join Today.