Java threading introduction  Thread-safety  Thread methods  Interruption  Thread scheduling  Context switching  Thread priorities  sleep()  yield()  Deadlock  Threading with Swing  invokeLater()  Thread pools  CoundDownLatch  ThreadPoolExecutor  CyclicBarrier

CyclicBarrier example: error handling

In our introduction to CyclicBarrier, we mentioned that one potentially useful feature is that if, while waiting at the barrier, one of the waiting threads is interrupted or times out, then all of the other waiting threads wake up with a BrokenBarrierException.

But what happens if an exception occurs during a thread's execution of the actual task code— in other words, while it isn't in the await() call? Well in this case, one solution is to deliberately interrupt the thread and then call await()! Our thread will immediately return from the await() call with an InterruptedException (which we obviously don't care about), and all other threads will wake up from await() with a BrokenBarrierException.

The pattern looks as follows:

private volatile Throwable error;
...
public void run() {
  try {
    ... perform task
    barrier.await();
    ... perform task
    barrier.await();
  } catch (InterruptedException e) {
    // don't care-- controller thread will get
    // BrokenBarrierException 
  } catch (BrokenBarrierException e) {
    // ditto
  } catch (Throwable t) {
    // some other exception occurred during our task
    error = t;
    Thread.currentThread().interrupt();
    try {
      barrier.await();
    } catch (Exception e) {}
  }
}

This is one of the few cases where we probably just want to "swallow" various exceptions, since the BrokenBarrierException will be thrown in all the other threads, including our "controller" thread. In the case of catching some other exception because it occurred during our task code, we may want to save it to a variable (as the error variable here). Then the controller thread, on being awoken with a BrokenBarrierException, can find the thread with the error variable set and throw the corresponding exception to the caller.

Error in the result amalgamation Runnable

There's actually a race condition that we haven't dealt with in the above code. When the last thread to get to the barrier calls the await() method, the barrier's Runnable (if any) is run before the await() method returns. If an unchecked exception is thrown by that Runnable, then the barrier is broken— in other words, other waiting threads potentially signalled— before that exception is thrown up to the caller of await(). In other words, the controller thread could be awoken before the error variable has been set.

To get round this problem, albeit in a slightly inelegant way, we can set another variable specially for errors that occur inside the Runnable. So that method looks as follows:

private volatile RuntimeException errorInAmalgRunnable;

CyclicBarrier barrier = new CyclicBarrier(noThreads, new Runnable() {
    public void run() {
      try {
        ... amalgamate results ...
      } catch (RuntimeException e) {
        errorInAmalgRunnable = e;
        throw e;
      }
    }
  });

You'll see elements of this error handling technique in our parallel sort implementation.


 Java threading articles  Java threading and concurrency  Java profiling  Java performance graph index

Unless otherwise stated, the Java programming articles and tutorials on this site are written by Neil Coffey. Suggestions are always welcome if you wish to suggest topics for Java tutorials or programming articles, or if you simply have a programming question that you would like to see answered on this site. Most topics will be considered. But in particular, the site aims to provide tutorials and information on topics that aren't well covered elsewhere, or on Java performance information that is poorly described or understood. Suggestions may be made via the Javamex blog (see the site's front page for details).
Copyright © Javamex UK 2016. All rights reserved.