Introduction
We are so spoiled by Java’s concurrency utilities that it’s easy to ignore some basic error handling like what happens when some of the threads we have created “go wild”.
It’s like getting on a fast motorcycle for the first time. Thrilled by the idea of speed you roll on the throttle reaching >200Km/h in no time but then a car crops up out of nowhere. It’s only then that you realize you have no idea how to apply the brakes. Nasty situation with a bad ending most of the times.
Similarly with concurrency, you can get something “working” pretty quick, but you still have to put some thought on how to deal with those threads in case something goes wrong!
Vanilla scenario
The most common examples involving threads are trivial in the sense that
- A thread pool is created
- Some relatively simple tasks are submitted to it
- Shutdown is issued in the end
- All results are available and you move on
That could look like this
|
|
We create a single-threaded pool and submit a task that just creates and returns a random String. Everything goes smooth. One worthy point to note here is that we need to properly clean-up after our thread pool by calling .shutdownAndAwaitTermination()
, otherwise the program will never exit.
Getting busy
Imagine now that the logic inside your thread enters an infinite while loop or gets into a situation that involves excessive CPU or memory usage. We’ll keep it simple here and go with a nice traditional infinite loop
|
|
The above can bring a server down on its own. Imagine an operation in the real world that operates on character level and accepts tons of text in a single request. Now on top of that picture a busy web application which accepts lots of similar requests and you have a recipe for disaster. Threads are busy consuming resources and your web server eventually crashes. Now what?
Please respond cut #1
First reaction is to add a timeout when waiting for the thread’s result as in
|
|
Still the program hangs. We got the TimeoutException
alright but the thread keeps running and chipping away memory.
Please respond cut #2
OK, Timeout won’t do it. Let’s be more pushy on that and also cancel the task - which sends an interruption message - after the timeout of 5sec.
|
|
Still nothing, but why? Java can and will deliver the interruption to the corresponding thread but you still must be responsive to interruption. We know that blocking methods are responsive to interruption (like .wait()
or BlockingQueue
’s put()
and take()
) but what about threads that do not make use of such methods.
Please respond cut #3
Remember that interruption is a co-operative mechanism, so we have to do our part. In order to make the above example work we need to react on the interruption
|
|
The interrupted status of the thread is set by future.cancel(true)
but one has to check for this using Thread.currentThread().isInterrupted()
and act accordingly. You also have to set the interrupted status back to the original value since we are not the owners of the thread.
Takeaways
Your concurrent applications will be far more robust if you ensure the following
- Make sure your threads are responsive to interruption
- Never forget to shutdown your thread pools, or register them to get called during JVM shutdown
Your need to tick both boxes above in order to properly clean up your threads and exit the JVM. This is especially important in CLI tools for example which run for some time and exit after each call. For long running applications like a web server you can register your shutdown hook through Runtime.getRuntime().addShutdownHook
.