CyclicBarrier not working as expected

1.5k views Asked by At

I am trying to simulate a triatlon competition using CyclicBarrier but it doesn't work as expected and I don't know why.

Each part of the competition has to wait till all the runners have completed the previous one, but it seems like it's waiting forever.

This is the piece of code for the phase one:

class Runner implements Runnable
{
    private CyclicBarrier bar = null;
    private static int runners;
    private static double[] time;
    private int number;
    public static String name;

    public Runner(int runners, String name)
    {
        time = new double[runners];
        for (int i=0; i<runners; i++)
            time[i] = 0;
        this.name= name;
    }

    public Runner(CyclicBarrier bar, int number)
    {   
        this.bar = bar;
        this.number = number;
    }

    public void run()
    {
        try { int i = bar.await(); } 
                   catch(InterruptedException e) {} 
                       catch (BrokenBarrierException e) {}
        double tIni = System.nanoTime();
        try { Thread.sleep((int)(100*Math.random()); } catch(InterruptedException e) {}
        double t = System.nanoTime() - tIni;
        time[number] += t;
    }
}

public class Triatlon
{
public static void main(String[] args)
{
    int runners = 100;
    CyclicBarrier Finish_Line_1 = new CyclicBarrier (runners);

    Runner c = new Runner(runners, "Triatlon");

    ExecutorService e = Executors.newFixedThreadPool(runners);

    for (int i=0; i<runners; i++)
        e.submit(new Runner(Finish_Line_1, i));

    System.out.println(Finish_Line_1.getNumberWaiting()); // this always shows 99
    try { int i = Finish_Line_1.await(); } 
           catch(InterruptedException e01) {} 
             catch (BrokenBarrierException e02) {}
    System.out.println("Swimming phase completed");

        // here the rest of the competition, which works the same way
}
}
2

There are 2 answers

0
Steve Sowerby On BEST ANSWER

CyclicBarrier cycles around once all parties have called await and the barrier is opened. Hence the name. So if you create it with 5 parties and there are 6 calls to await the last one will trigger it to be waiting again for 4 more parties to join.

That's basically what happens here as you have the 1 extra await call in your main. It is waiting for another runners-1 calls to happen.

The simple fix is to create the CyclicBarrier with runners+1 parties.

8
Marko Topolnik On

You have an off-by-one error: you create a CyclicBarrier for 100 threads, but execute 101 awaits, the one-off being in the main method. Due to the semantics of the cyclic barrier, and subject to nondeterministic conditions, your main thread will be the last to execute await, thereby being left alone waiting for another 99 threads to join in.

After you fix this problem, you'll find out that the application keeps running even after all work is done. This is because you didn't call e.shutdown(), so all the threads in the pool stay alive after the main thread is done.

BTW getNumberWaiting always shows 0 for me, which is the expected value after the barrier has been lowered due to 100 submitted threads reaching it. This is nondeterministic, however, and could change at any time.