Exception Handling and Task Result
You can retrieve the task result using either the
Future.get() or the
Future.get(long timeout, TimeUnit unit) method. The
no-argument method blocks other tasks until the task completes by normal execution, by cancellation, or by throwing an exception. Use this method with caution, because it will wait indefinitely for a task to complete. The
Future.get() method with a
timeout parameter is more useful because it will cause a method to return after a specified period of time:
for (Future<Integer> f : futurList) {
try {
Integer result = f.get(1000, TimeUnit.MILLISECONDS);
Logger.log(f + " result. Processed orders " + result);
} catch (InterruptedException e) {
Logger.error(e.getMessage(), e);
} catch (ExecutionException e) {
Logger.error(e.getCause().getMessage(), e);
} catch (TimeoutException e) {
Logger.error(e.getMessage(), e);
} catch (CancellationException e) {
Logger.error(e.getMessage(), e);
}
// to avoid printing completed tasks, you may want to remove
// the completed task from futureList here
}
The Future.get() method throws different exceptions to give an exact reason for each task failure:
- InterruptedException is thrown if a thread is interrupted while waiting for computation results.
- TimeOutException is thrown if a result cannot be retrieved in the specified period of time.
- CancellationException is thrown if computation is cancelled.
- ExecutionException is thrown if a task computation fails by throwing any exception, including runtime exceptions. This ensures that the executor threads will not be terminated due to an exception in task execution. The side effect of this behavior is that in application scenarios where you want to propagate a task runtime exception, you need to retrieve the runtime exception from ExecutionException and throw it again.
Putting the Pieces Together
The OrderProcessorMain class (see
Listing 3) uses all the concepts discussed in the previous sections. In particular, the
main() method performs the following steps:
- Creates and configures the CustomThreadPoolExecutor object
- Creates and submits the OrderProcessorCallable tasks
- Populates the order-blocking queue with OrderVO objects
- Prints processor status at regular intervals until the order-blocking queue is empty
- Shuts down the thread pool executor
- Prints the task result after task completion
To see the sample application in action, just run the OrderProcessorMain.main(String[] args) method. For convenience, the source code download provided with this article can be imported as an Eclipse project.