Why is CyclicBarrier reset () method throwing BrokenBarrierException
My goal is very simple. I want to use the method CyclicBarrier
and reset()
to run 3 threads 4 times using the code below. Explored all possible resources on the web, in Concurrency in practice and in thinking in Java. Couldn't solve as I want. Thinking in Java, there is one solution of this kind in HorseRace.Java, but it used the Executor service. I want to do this with methods CyclicBarrier
and reset()
. Here is my code along with an exit that continues to the end but throws BrokenBarrierException
after the reset () method.
package com.apal.barrier;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierEx1 {
CyclicBarrier cb;
public static int count = 0;
public static void main(String[] args) {
new CyclicBarrierEx1().manageThread();
}
private void manageThread() {
cb = new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
if (count == 3) {
System.out.println("Exit from system");
return;
}
System.out.println("Collating task");
cb.reset();
for (int i = 0; i < 3; i++) {
new Thread(new Worker(cb)).start();
}
count++;
}
});
for (int i = 0; i < 3; i++) {
new Thread(new Worker(cb)).start();
}
}
}
class Worker implements Runnable {
CyclicBarrier cb;
public Worker(CyclicBarrier cb) {
this.cb = cb;
}
@Override
public void run() {
doSomeWork();
try {
cb.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
private void doSomeWork() {
System.out.println("Doing some work ");
}
}
Sample output
Doing some work
Doing some work
Doing some work
Collating task
Doing some work
Doing some work
Doing some work
Collating task
Doing some work
java.util.concurrent.BrokenBarrierExceptionDoing some work
Doing some work
Collating task
Doing some work
at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
at java.util.concurrent.CyclicBarrier.await(Unknown Source)
at com.apal.barrier.Worker.run(CyclicBarrierEx1.java:48)
at java.lang.Thread.run(Unknown Source)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
at java.util.concurrent.CyclicBarrier.await(Unknown Source)
Doing some work at com.apal.barrier.Worker.run(CyclicBarrierEx1.java:48)
at java.lang.Thread.run(Unknown Source)
Doing some work
Exit from systemjava.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
at java.util.concurrent.CyclicBarrier.await(Unknown Source)
at com.apal.barrier.Worker.run(CyclicBarrierEx1.java:48)
at java.lang.Thread.run(Unknown Source)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
at java.util.concurrent.CyclicBarrier.await(Unknown Source)
at com.apal.barrier.Worker.run(CyclicBarrierEx1.java:48)
at java.lang.Thread.run(Unknown Source)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
at java.util.concurrent.CyclicBarrier.await(Unknown Source)
at com.apal.barrier.Worker.run(CyclicBarrierEx1.java:48)
at java.lang.Thread.run(Unknown Source)
java.util.concurrent.BrokenBarrierException
at java.util.concurrent.CyclicBarrier.dowait(Unknown Source)
at java.util.concurrent.CyclicBarrier.await(Unknown Source)
at com.apal.barrier.Worker.run(CyclicBarrierEx1.java:48)
at java.lang.Thread.run(Unknown Source)
source to share
You have a race condition where the barrier action is executed, which is passed in the constructor CyclicBarrier
. The docs forCyclicBarrier.await()
say about this method of action (emphasis mine):
If the current thread is the last thread, and a non-null barrier action was provided in the constructor, then the current thread starts the action before allowing other threads to continue .
This means that a call to the barrier action method reset()
cannot occur while these other threads are still waiting at the barrier. This will result in BrokenBarrierException
.
See the documentation for the paragraph that begins with "If the action of the barrier is independent of whether the parties are suspended when it is executed, then any thread in the batch can take that action when it is released." Using this method, you can perform the work that you are currently doing in an action routine within one of the worker threads as soon as it is freed from await()
. Below is an untested attempt (note - I also restarted where the variable is count
incremented to avoid a race condition in which worker threads might terminate before getting count
close to increasing):
package com.apal.barrier;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierEx1 {
CyclicBarrier cb;
public static int count = 0;
public static void main(String[] args) {
new CyclicBarrierEx1().manageThread();
}
public static void barrierComplete(CyclicBarrier cb) {
System.out.println("Collating task");
if (count == 3) {
System.out.println("Exit from system");
return;
}
count++;
for (int i = 0; i < 3; i++) {
new Thread(new Worker(cb)).start();
}
}
private void manageThread() {
cb = new CyclicBarrier(3);
for (int i = 0; i < 3; i++) {
new Thread(new Worker(cb)).start();
}
}
}
class Worker implements Runnable {
CyclicBarrier cb;
public Worker(CyclicBarrier cb) {
this.cb = cb;
}
@Override
public void run() {
doSomeWork();
try {
if (cb.await() == 0) {
CyclicBarrierEx1.barrierComplete(cb);
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
private void doSomeWork() {
System.out.println("Doing some work ");
}
}
source to share
Took an idea from MichaelBurr's tip to check for no pending number of threads in the worker thread before resetting the CyclicBarrier synchronizer. I am posting my own answer because I really wanted to use the CyclicBarrier and reset () method to achieve multiple threads starting and matching their tasks, such as Matrix manipulation.
package com.apal.barrier;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierEx1 {
CyclicBarrier cb;
public static int count = 0;
public static void main(String[] args) {
new CyclicBarrierEx1().manageThread();
}
private void manageThread() {
cb = new CyclicBarrier(3, new Runnable() {
@Override
public void run() {
if (count == 3) {
System.out.println("Exit from system");
return;
}
System.out.println("Collating task");
count++;
// cb.reset(); **Commented and replaced in Worker**
for (int i = 0; i < 3; i++) {
new Thread(new Worker(cb)).start();
}
}
});
for (int i = 0; i < 3; i++) {
new Thread(new Worker(cb)).start();
}
}
}
class Worker implements Runnable {
CyclicBarrier cb;
public Worker(CyclicBarrier cb) {
this.cb = cb;
}
@Override
public void run() {
doSomeWork();
try {
cb.await();
//if (cb.getNumberWaiting() == 0) // **if no one is waiting, then reset it.**
// cb.reset();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
private void doSomeWork() {
System.out.println("Doing some work ");
}
}
Sample output
Doing some work
Doing some work
Doing some work
Collating task
Doing some work
Doing some work
Doing some work
Collating task
Doing some work
Doing some work
Doing some work
Collating task
Doing some work
Doing some work
Doing some work
Exit from system
From the Javadoc for reset ()
Resets the barrier to its original state. If any parties are currently waiting at the barrier, they will return with BrokenBarrierException. Note that resetting after a break occurs for other reasons may be difficult to complete; threads must re-sync in a different way and choose one to reset. It may be preferable to create a new barrier for later use.
So reset causes any currently waiting threads to throw a BrokenBarrierException and wake up immediately. reset is used when you want to "break" the barrier.
You cannot use reset () under normal circumstances.
source to share