Search code examples
javamultithreadingwaitbarriercyclicbarrier

Java looping Threads using CyclicBarrier


I have a program with this general structure:

init
create CyclicBarrier
initialise all threads, attaching to barrier
*start all threads*
wait for join
display stats


*start all threads*
perform calculation
await barrier

My problem is I need the threads' run() method to keep looping until a certain condition is met, but pausing after every iteration to let all threads synchronise.

I've already tried attaching a Runnable method to the barrier, but this ends up requiring the recreation and restarting of each thread, which isn't a very good solution.

I've also tried using the CyclicBarrier's reset() method, but this just seems to cause errors on the existing threads, even when executed after all threads have completed.

My question is:

-Is it possible to 'reset' a barrier and have all the barrier's threads follow the same conditions as they did before the first invocations of await()?

-Or is there another method I should be using to achieve this?

Thanks in advance


Solution

  • Following @Totoro's answer, below is a little bit of example code which also incorporates the requirement "I need the threads' run() method to keep looping until a certain condition is met, pausing after every iteration to let all threads synchronise". That makes it complex pretty quick, but hopefully the program output will clarify the example code (or I should just make better examples).

    import java.util.concurrent.CyclicBarrier;
    import java.util.concurrent.atomic.AtomicBoolean;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class BarrierCalc implements Runnable {
    
    public static final int CALC_THREADS = 3;
    
    private static final AtomicBoolean runCondition = new AtomicBoolean();
    private static final AtomicBoolean stopRunning = new AtomicBoolean();
    
    public static void main(String[] args) {
    
        CyclicBarrier barrier = new CyclicBarrier(CALC_THREADS + 1);
        for (int i = 0; i < CALC_THREADS; i++) {
             new Thread(new BarrierCalc(barrier)).start();
        }
        try {
            runCondition.set(true);
            barrier.await();
            showln(0, "STATS!");
    
            barrier.await();
            showln(0, "start looping 1");
            Thread.sleep(200);
            runCondition.set(false);
            showln(0, "stop looping 1");
            barrier.await();
            runCondition.set(true);
    
            barrier.await();
            showln(0, "start looping 2");
            Thread.sleep(100);
            runCondition.set(false);
            showln(0, "stop looping 2");
            barrier.await();
    
            stopRunning.set(true);
            showln(0, "finishing");
            barrier.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private static final AtomicInteger calcId = new AtomicInteger();
    
    private CyclicBarrier barrier;
    private int id;
    
    public BarrierCalc(CyclicBarrier barrier) {
        this.barrier = barrier;
        id = calcId.incrementAndGet();
    }
    
    public void run() {
    
        showln(id, "waiting for start");
        try {
            barrier.await(); // display stats
            barrier.await(); // start running
            int loopNumber = 0;
            while (!stopRunning.get()) {
                showln(id, "looping " + (++loopNumber));
                while (runCondition.get()) {
                    Thread.sleep(10); // simulate looping
                }
                showln(id, "synchronizing " + loopNumber);
                barrier.await();
                showln(id, "synchronized " + loopNumber);
                // give main thread a chance to set stopCondition and runCondition
                barrier.await();
            }
            showln(id, "finished");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    private static final long START_TIME = System.currentTimeMillis();
    
    public static void showln(int id, String msg) {
        System.out.println((System.currentTimeMillis() - START_TIME) + "\t ID " + id + ": " + msg);
    }
    
    }
    

    Keep in mind that program output might not be in the order expected: threads that are writing at the same time to one synchronized output (System.out) are given write-access in random order.