Introduction - The Saga Continues
In
Part 1 we discussed presenting a cleaner interface to Java 6's
ExecutorService and
ScheduledExecutorService. In
Part 2 we discussed ensuring that the system doesn't eat / ignore exceptions in threads. In Part 3 we will go over reusing the
thread pool... because that's the point of a thread pool.
Background
We don't read ALL of the documentation on a given class, we read parts and whatever the IDE gives
|
Thread Pool, not to be confused with Deadpool |
us on a mouse-over. This habit led me to reading the method-level documentation of ExecutorService but not the class-level documentation. This is important later.
Hypothesis
The
ExecutorService
will have an equivalent of
Thread.join(), and then be re-usable since that is the whole point of a thread pool. A quick skim of
shutdown() says to use
awaitTermination(). Upon reading the full docs of
awaitTermination()
it appears that this is what I am looking for.
Results
Running threads after a while mysteriously fail with a ThreadPool size of 0. I never set the size to 0, why would it do that? After struggling with this and creating more unit tests to narrow the problem down for a full day (full day) I find that
awaitTermination()
terminates the WHOLE POOL instead of terminating the currently running threads. What's the point of having a terminated pool? Isn't that what
close() is for? Does this have a
close()
? Why not? Maybe because it is in the
java.io
package instead of
java.concurrent
? Again, the entire point of having a thread pool is to reuse the threads!
Conclusion
In addition to needing to keep a
List<Callable<Void>>
I need to keep a
List<Future<Void>>
and loop through all of the
Futures
to
cancel()
each one. WHAT? Again, nothing in the documentation of
awaitTermination()
says that it terminates the thread pool itself. That is buried in the class level documentation. I end up adding a
cancelThreads()
method to our custom ThreadPool object leaving us with a final API like the following:
public class ThreadPool {
ThreadPool(int size) {...}
void add(Runnable) {...}
void add(Callable<Void>){...}
void clear(){...}
boolean isEmpty(){...}
void runSynchronously(){...}
void runAsynchronously(){...}
Callable<Void> toSafeCallable(Callable<Void>){...}
Runnable toSafeRunnable(Runnable){...}
void cancelThreads(){...}
void close(){...}
No comments:
Post a Comment