Search code examples
javamultithreadingconcurrencybarrier

Does barrier (e.g. CyclicBarrier) cause deadlock when the amount of thread passed it is smaller than the barrier limit?


When running the following code, the 2 start threads will be locked by the CyclicBarrier *object and waiting for the third thread infinitely to be unlocked

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class MainDeadlock {
  public static void main(String[] args) throws InterruptedException {
    final CyclicBarrier c = new CyclicBarrier(3); 
    Runnable r = () -> {
            try {
                c.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println("Run!");
    };
    new Thread(r).start();
    new Thread(r).start();
}

}

So the 2 started thread are waiting for the third third to resolve this barrier. However, according to the Java API documentation of CyclicBarrier, CyclicBarrier is

A synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point

I am confused on how they "wait for each other",

Questions: does "wait for each other" imply a circular wait? If so, how? Strictly speaking, is this a deadlock situation?


Solution

  • You can think of CyclicBarrier as not knowing about threads at all, per-se. Think of it like this:

    1. The barrier maintains a tally of calls to await().
    2. When await() is invoked, the code blocks (the method does not return), but the barrier increases its tally.
    3. When the tally reaches the parties value given on construction, the tally is reset, and all threads which are blocked in a call to await() are released (i.e. the method returns).

    So, in your situation, calls to await() won't return until a third invocation occurs, so your 2 existing threads are effectively stuck. This isn't technically a deadlock as it can be got out of easy enough (by making another call to await()).

    The reason it is called cyclic is because, once the tally has been reset and the threads have been released, it can be used again. A typical usage would have the parties set to the number of threads that are going to synchronize on it, and those threads all enter some kind of loop, whereby the barrier is used to ensure that no thread moves on to the next iteration until all other threads have also completed the current iteration.