Reputation: 61
I'm trying to write a process in Java that executes a series of tasks concurrently, waits for the tasks to be done, then tags the overall process as complete. Each task has its own information, including when the individual task is complete. I'm using an ExecutorService for the process, and have boiled down the essence of the process as follows:
List<Foo> foos = getFoos();
ExecutorService executorService = Executors.newFixedThreadPool(foos.size());
for (Foo foo : foos) {
executorService.execute(new MyRunnable(foo));
}
executorService.shutdown();
try {
executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// log the error.
}
completeThisProcess();
Each of the MyRunnable objects has a run method that makes a webservice call, then writes the results of the call to the database, including the time the call completed. The completeThisProcess method simply writes the status of the whole process as complete along with the time the process completed.
The problem I'm having is that when I look in the database after the process has completed, the completeThisProcess method has apparently been able to execute before all of the MyRunnables have completed. I'm noticing that the times that are written from the completeThisProcess method are even occasionally upwards of 20-30 seconds before the last MyRunnable task has completed.
Is there anything there that is obviously wrong with the process I've written? Perhaps I'm not understanding the ExecutorService correctly, but I thought that the awaitTermination method should be ensuring that all of the MyRunnable instances have completed their run methods (assuming they complete without exception, of course), which would result in all the sub-tasks having completion times before the overall process's completion time.
Upvotes: 4
Views: 1330
Reputation: 1194
If you want to wait for all the threads to return is then following method can be trusted.
Make your thread class implement Callable
interface instead of Runnable
(In case of Callable
run
method will return some value. Make it return threadName.
Create a list of Callable
Objects and use invokeAll method which will wait for all threads to return. For the below code assume the thread class name to be MyCallable.
ExecutorService executorService = Executors.newFixedThreadPool(foos.size());
List<Callable> tasks = new ArrayList<>();
for (Foo foo : foos) {
tasks.add(new MyCallable(foo));
}
executorService.invokeAll(tasks);
invokeAll returns List of future objects if you want to make use of it.
OR
you can use CountDownLatch
.
CountDownLatch cdl = new CountDownLatch(foo.size);
Make it count down in run method using cdl.countDown()
method.
Use cdl.await
after for loop and then it will wait untill cdl become zero.
Upvotes: 2