Thursday, September 6, 2012

ExecutorService, Part 2, the "Unhappy Path"

In a previous post, I explained how to parallelize your algorithms using an ExecutorService.  Here we consider the unhappy path when your algorithms throw Exceptions, or you wish to stop their execution.  This requires adding a few lines of code (a try - catch block) to the calculateInParallel( ) method.



 try {
List<Future<CallableCalculation>> results = myService.invokeAll(tasks);
   for (Future<CallableCalculation> future : results) {
      CallableCalculation cc = future.get();       
      resultMap.put(cc.year, cc.getPercentileResults());
   } 
   }
   catch (ExecutionException ee) {
      // what to do?
   }
   catch (InterruptedException ie) {
      // what to do???
   }
But "what to do"? You probably want to shut down the ExecutorService and return some status for failure or interruption. To handle this, plus some of the other "utility" methods like calculating a "good" number of threads to use, I wrote a class CallablesCaller, which handles this common utility work, (in case of failure it returns null for the List of results, and also has a wasInterrupted() method), while preserving the key idea of submitting a list of Callables to an ExecutorService. The source code (two files, you also need ConcurrentUtil) is available here, and the unit test is here.

Enjoy, and I hope you find this useful in your efforts.  Note that CallablesCaller does not do anything fancy to shutdown immediately if any of the Callables fail.  This is because it uses the simple, basic invokeAll() method.  If you wanted to stop ASAP on a failure, you'd want to add on an ExecutorCompletionService so that you could poll the results as they arrive and handle failures immediately.  I didn't add this complication because the algorithms I am using are simple statistics that hardly ever fail, and, even if one does fail, the other results are still useful.