ExecutorService, how to wait for all tasks to finish
Solution 1
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.
Solution 2
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.
Solution 3
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.
Solution 4
If you want to wait for the executor service to finish executing, call shutdown()
and then, awaitTermination(units, unitType), e.g. awaitTermination(1, MINUTE)
. The ExecutorService does not block on it's own monitor, so you can't use wait
etc.
Solution 5
You could wait jobs to finish on a certain interval:
int maxSecondsPerComputeDTask = 20;
try {
while (!es.awaitTermination(uniquePhrases.size() * maxSecondsPerComputeDTask, TimeUnit.SECONDS)) {
// consider giving up with a 'break' statement under certain conditions
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Or you could use ExecutorService.submit(Runnable) and collect the Future objects that it returns and call get() on each in turn to wait for them to finish.
ExecutorService es = Executors.newFixedThreadPool(2);
Collection<Future<?>> futures = new LinkedList<<Future<?>>();
for (DataTable singleTable : uniquePhrases) {
futures.add(es.submit(new ComputeDTask(singleTable)));
}
for (Future<?> future : futures) {
try {
future.get();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
InterruptedException is extremely important to handle properly. It is what lets you or the users of your library terminate a long process safely.
Related videos on Youtube
george smiley
Updated on February 22, 2022Comments
-
george smiley about 2 years
What is the simplest way to to wait for all tasks of
ExecutorService
to finish? My task is primarily computational, so I just want to run a large number of jobs - one on each core. Right now my setup looks like this:ExecutorService es = Executors.newFixedThreadPool(2); for (DataTable singleTable : uniquePhrases) { es.execute(new ComputeDTask(singleTable)); } try{ es.wait(); } catch (InterruptedException e){ e.printStackTrace(); }
ComputeDTask
implements runnable. This appears to execute the tasks correctly, but the code crashes onwait()
withIllegalMonitorStateException
. This is odd, because I played around with some toy examples and it appeared to work.uniquePhrases
contains several tens of thousands of elements. Should I be using another method? I am looking for something as simple as possible -
ThanksForYourHelp almost 14 yearsshutdown() stops the ExecutorService from accepting new tasks and closes down idle worker threads. It is not specified to wait for the shutdown to complete and the implementation in ThreadPoolExecutor does not wait.
-
NG. almost 14 years@Alain - thanks. I should have mentioned awaitTermination. Fixed.
-
NG. almost 14 yearsI think it's awaitTermination.
-
mdma almost 14 years@SB - Thanks - I see my memory is fallible! I've updated the name and added a link to be sure.
-
andersoj almost 14 yearsThis is a good counterpoint to my answer -- I'd say the straightforward answer to the question is invokeAll(); but @seh has it right when submitting groups of jobs to the ES and waiting for them to complete... --JA
-
Rag about 12 yearsWhat if in order for a task to finish it must schedule further tasks? For example, you could make a multithreaded tree traversal which hands off branches to worker threads. In that case, since the ExecutorService is shut down instantly it fails to accept any recursively scheduled jobs.
-
NG. about 12 yearsThat becomes an interesting problem. One solution is you could utilize a separate executor service for child tasks only, and not expose it to users. That might be inefficient though with the additional threads. It sounds like you might need some more advanced life time management of the executor to determine when it is safe to call shutdown.
-
rogerdpack about 11 yearsunfortunately after using shutdown, you can never schedule any more tasks, so basically have to new up your ExecutorService again, but it may be worth the trade off...if you have a stopping point where you know you won't be adding any new future threads, that is...
-
Abs about 11 years
awaitTermination
requires timeout time as a parameter. While it's possible to provide a finite time and place a loop around it to wait until all threads have finished, I was wondering if there was a more elegant solution. -
NG. about 11 yearsYou are right, but see this answer - stackoverflow.com/a/1250655/263895 - you could always give it an incredibly long timeout
-
subrat71 over 10 years@om-nom-nom, thank you for updating the links. I am glad to see that the answer is still useful.
-
Desty over 10 yearsThis is perfect if you're adding all of your jobs in a batch and you hang onto the list of Callables, but it won't work if you're calling ExecutorService.submit() in a callback or event-loop situation.
-
David Mann over 10 yearsExecutorServices makes things simpler
-
rogerdpack over 9 yearsTo wait "forever," use it like
awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
stackoverflow.com/a/1250655/32453 -
Vic about 9 yearsGood answer, I wasn't aware of
CompletionService
-
ToolmakerSteve over 8 yearsThis is the approach to use, if you don't want to shutdown an existing ExecutorService, but just want to submit a batch of tasks, and know when they are all finished.
-
Shervin Asgari about 8 yearsI think this is the easiest approach
-
John29 about 8 yearsI think it's worth mentioning that shutdown() still should be called when the ExecutorService is no longer needed, otherwise the threads will never terminate (except for the cases when corePoolSize=0 or allowCoreThreadTimeOut=true).
-
MohamedSanaulla about 7 yearsamazing! Just what I was looking for. Thanks a lot for sharing the answer. Let me try this out.
-
Jaime Hablutzel over 5 years@NG., at first sight it seems that the
awaitTermination
API is incomplete because of not providing a way to allow to wait indefinitely. -
Jaime Hablutzel over 5 years@MosheElisha, are you sure?. docs.oracle.com/javase/8/docs/api/java/util/concurrent/… says Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.
-
MosheElisha over 5 years@JaimeHablutzel You are correct. I just tested it also and I believe I was wrong. I will delete my comment so it won't confuse people.
-
MDT almost 4 yearsDoc Link is dead
-
Marcell about 3 yearsDon't forget to catch the InterruptedException while awaiting.
-
TommyQu almost 3 years@Desty In such situation, what will be the best way to implement?