It is commonly said that a good framework makes common tasks easy to do, while keeping hard things possible. Today, I'm reminded that a good related property is to prevent common errors.
My friend and fellow developer Jason posts today a chunk of hard-learned wisdom about spinning off worker threads to perform asynchronous tasks in an ASP.NET application. It seems that if an exception bubbles up to the top of the call stack, not only does the thread die (which is expected), but it takes down the entire ASP.NET application, forcing it to restart, leading to a loss of in-memory session state. OUCH, yes, and it's an easy bug to introduce.
Making easy errors hard to make
This is why I'm thankful for the general design Java's Executor Framework imposes on the developer, as it renders this sort of bug a bit harder to make.
Using an executor service with the Callable
You create a task to offload, as an instance of a Callable
, with a call() method that's allowed to throw any Exception it wants.
You submit your Callable
to an executor service, which spins off a thread for it if required, and immediately returns you a Future instance, while the worker thread does its thing.
Later, using the handle of that Future
instance, you can query the result of the computation from your main thread by calling get() on it. If the task you spun off threw an exception from call(), you get the exception only then (wrapped in an ExecutionException), on the main thread, where you're actually able to (and forced to) deal with it, since Future.get() declares itself as throwing ExecutionException.