Threading is easy in Java, but today I was reminded of another of its pitfalls.
In Java- or container-provided threads, unhandled exceptions from our code will be printed or logged to the console. Create your own
Thread or use
ThreadPoolExecutor, and it’s a different story..
Threaded code tends to die silently. Nothing on the console or logs. Unhandled exceptions are invisible, and leave very few clues. Let’s look at why.
Request-handling and Event-Handling loops
Web/ EJB containers, Swing and AWT applications might seem very different. But they all have a common basis, as they are all forms of event loop.
Processing a single request or event has the possibility of failure. Processing multiple must therefore include a reliability boundary, to isolate failures in a single request.
Well-behaved containers such a EJB/servlet containers, the AWT event-handler and the Java main thread all provide a reliability boundary in the form of a
try/ catch block which logs exceptions.
(The Java main thread is actually a process boundary, rather than an event loop. But as such a reliability boundary is still present.)
So in our normal daily coding, we are mostly writing code in the context of such a container- or platform-provided thread. No need to do anything special, any exceptions will show up in the logs.
But when we take these practices & start writing threaded code, we are in for a problem..
Thread, SwingWorker, FutureTask & ExecutorService
Writing threaded code has many challenges — synchronization, race conditions, proper division of work.. But perhaps the most basic requirement, is for a developer to be able to see any exceptions that occur!
When creating your own
Thread or using
ExecutorService, the responsibility is yours to specifically examine how this task will get executed and ensure that exceptions will be logged.
Here are some basic recipes for safety:
|Ways to make safe
The approaches vary somewhat, but the basic principle here is to either surround every threaded code block with try/catch manually, or to use a handler/ define a subclass to do this for us.
Obviously in any modest-size application, an automated approach is preferred. Coding
try/catch manually requires much repeated code, and has a high likelihood of some blocks being missed.
ThreadPoolExecutor and friends are a bit different here. It already provides a method we can override, called on the successful or failed completion of tasks. The only difficulty here is checking if the Runnable is a
Future, and if so getting any exception.
FutureTask may require special checking, as they hold exceptions internally. Calling
get() throws any enclosed exception wrapped in an ExecutionException. See the Javadoc.
Java library design
Now, considering the design of the Java libraries. Why was it done this way?
There are multiple situations here, where Java library code could have included diagnostics & “print to System.err” behavior for unhandled exceptions.
This would, of course, greatly assist application coding & use of these constructs — making exceptions visible, without requiring more boilerplate.
On the other side of the equation, is the need for Java to provide fundamental & general-purpose building blocks. These have to be usable to build platforms, frameworks, libraries and indeed other languages.
This argument says that exception-handling and the method of logging should not be prescribed, and should be provided by the user.
There is some middle ground — a “third way” — in
FutureTask. Unfortunately this makes the exception harder to get out & diagnose than ever!
My assessment, leans towards better help for application code. Java main thread does it — why not be consistent? There would be minimal cost & much gain in providing useful “default” handlers.
Have you been bitten by exceptions in threaded code? Should the Java libraries handle this better? Add your comment now.