ExecutorService, how to wait for all tasks to finish
The simplest approach is to use ExecutorService.invokeAll()
which does what you want in a one-liner. In your parlance, you'll need to modify or wrap ComputeDTask
to implement Callable<>
, which can give you quite a bit more flexibility. Probably in your app there is a meaningful implementation of Callable.call()
, but here's a way to wrap it if not using Executors.callable()
.
ExecutorService es = Executors.newFixedThreadPool(2);List<Callable<Object>> todo = new ArrayList<Callable<Object>>(singleTable.size());for (DataTable singleTable: uniquePhrases) { todo.add(Executors.callable(new ComputeDTask(singleTable))); }List<Future<Object>> answers = es.invokeAll(todo);
As others have pointed out, you could use the timeout version of invokeAll()
if appropriate. In this example, answers
is going to contain a bunch of Future
s which will return nulls (see definition of Executors.callable()
. Probably what you want to do is a slight refactoring so you can get a useful answer back, or a reference to the underlying ComputeDTask
, but I can't tell from your example.
If it isn't clear, note that invokeAll()
will not return until all the tasks are completed. (i.e., all the Future
s in your answers
collection will report .isDone()
if asked.) This avoids all the manual shutdown, awaitTermination, etc... and allows you to reuse this ExecutorService
neatly for multiple cycles, if desired.
There are a few related questions on SO:
None of these are strictly on-point for your question, but they do provide a bit of color about how folks think Executor
/ExecutorService
ought to be used.
If you want to wait for all tasks to complete, use the shutdown
method instead of wait
. Then follow it with awaitTermination
.
Also, you can use Runtime.availableProcessors
to get the number of hardware threads so you can initialize your threadpool properly.
If waiting for all tasks in the ExecutorService
to finish isn't precisely your goal, but rather waiting until a specific batch of tasks has completed, you can use a CompletionService
— specifically, an ExecutorCompletionService
.
The idea is to create an ExecutorCompletionService
wrapping your Executor
, submit some known number of tasks through the CompletionService
, then draw that same number of results from the completion queue using either take()
(which blocks) or poll()
(which does not). Once you've drawn all the expected results corresponding to the tasks you submitted, you know they're all done.
Let me state this one more time, because it's not obvious from the interface: You must know how many things you put into the CompletionService
in order to know how many things to try to draw out. This matters especially with the take()
method: call it one time too many and it will block your calling thread until some other thread submits another job to the same CompletionService
.
There are some examples showing how to use CompletionService
in the book Java Concurrency in Practice.