A CyclicBarrier
is a synchronization aid that allows a set of threads to all wait for each other to reach a common barrier point. Suppose that in a test, there is a Runnable
which needs to be executed by multilpe threads at the same time for multiple rounds. Then the beginning and ending for each round can be coordinated by a CyclicBarrier
that a manager thread can signal the beginning of a round to all the worker threads, and wait until all the worker threads finish to end the round. The beginning and ending can then happen for more rounds.
Basically, the manager thread and all the worker threads need to call CyclicBarrier.await()
for both the beginning and the ending of a round. Problems could happen if a worker thread encounters a RuntimeException
during a round and the RuntimeException
is not caught. The worker thread will not reach the ending of the round and other worker threads and the manager thread will be waiting forever. The question arises here is how can a worker thread let other threads know when the worker thread encounters a RuntimeException
. This post discusses a solution using the CyclicBarrier
itself which is already coordinating all the threads.
CyclicBarrier
s use an all-or-none breakage model for failed synchronization attempts: If a thread leaves a barrier point prematurely, all other threads waiting at that barrier point will also leave abnormally and receive either a BrokenBarrierException
or an InterruptedException
. This behavior implies that if somehow we can break the CyclicBarrier
in question after a RuntimeException
is caught, other threads will know even if the RuntimeException
is re-thrown.
To break a CyclicBarrier
, what we can do is "leave a barrier point prematurely". For the worker thread encountering a RuntimeException
, the worker thread is not at the barrier point yet. To make it get to a barrier point and also leave prematurely, we let it call CyclicBarrier.await()
with the interrupted status set to true
. The interrupted status is set by the worker thread itself via interrupting itself. To avoid side effects, we also save and restore the existing interrupted status before and after breaking the CyclicBarrier
, respectively.
-
public class CyclicBarrierUtility {
-
-
public static void breakCyclicBarrier(final CyclicBarrier barrier) {
-
if(Thread.currentThread().isInterrupted()) {
-
try {
-
barrier.await();
-
} catch(final BrokenBarrierException unused) {
-
Thread.currentThread().interrupt();
-
} catch(final InterruptedException unused) {
-
Thread.currentThread().interrupt();
-
}
-
} else {
-
Thread.currentThread().interrupt();
-
try {
-
barrier.await();
-
} catch(final BrokenBarrierException unused) {
-
Thread.interrupted();
-
} catch(final InterruptedException unused) {
-
Thread.interrupted();
-
}
-
}
-
}
-
}
If no RuntimeException
is encountered, the manager thread can still choose to break the CyclicBarrier
to signal the ending of all rounds to the worker threads. This choice makes breaking the CyclicBarrier
the consistent exit point for both normal and abnormal scenarios.
Post new comment