The first thing I noticed is that my engine was taking over 40 seconds to shut down after a Ctrl-C. Where was the interruption going? It turns out that the forEach lambda, apparently as part of it's functionalness, completely ignores interruptions until it is completely done with its tasks. This means that the new stream APIs are not useful for long-running tasks (e.g. the kinds of tasks you want to run in parallel in the first place) without a work-around, such as checking manually if a thread is interrupted or a thread pool is shutdown (see below).
The second thing I noticed that was when a Callable was being executed by and the executor service was shut down, the Callable's Thread.currentInstance().isInterrupted() would return false, even if actively checked! As a work-around I had to pass a reference to the executor service into the Callable and check ExecutorService.isShutdown() instead. In my particular code-base this caused a circular reference but the modern garbage collector uses mark-and-sweep instead of reference counting so no memory leaks happen.
It could be worse [1]. |
Lastly the APIs can't decide if they want Runnables or Callable
The solution was to create a ThreadPool class and ScheduledThreadPool sub class in my project's utils module to simplify all of this complexity and automatically convert between Runnables and Callables as needed. After I made these classes and had the code base use them it was easier to think about how the system worked instead of being mired in the details of the Java API.
[1] Dragon Riders of Pern by FanDragonBall
No comments:
Post a Comment