Four Ways to Determine When a Java ThreadPool Has Completed All Tasks
This article explains four practical techniques—using isTerminated, comparing task counts, CountDownLatch, and CyclicBarrier—to reliably detect when a Java ThreadPoolExecutor has finished executing all submitted tasks, including code examples, advantages, disadvantages, and a summary of each method.
In many scenarios we need to wait until all tasks submitted to a ThreadPoolExecutor finish before proceeding; unlike a simple Thread where join() suffices, checking a thread pool’s completion is more involved.
The article presents four methods to determine whether a thread pool has completed its work:
Using isTerminated()
Using getCompletedTaskCount() and getTaskCount()
Using a CountDownLatch
Using a CyclicBarrier
Method 1: isTerminated
Call shutdown() on the pool and then loop while !threadPool.isTerminated() . This requires closing the pool, which may be undesirable in some cases.
public static void main(String[] args) {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
addTask(threadPool);
isCompleted(threadPool);
System.out.println("ThreadPool tasks completed!");
}
private static void isCompleted(ThreadPoolExecutor threadPool) {
threadPool.shutdown();
while (!threadPool.isTerminated()) {
// wait
}
}Pros: Simple logic.
Cons: Must shut down the pool; not suitable if the pool should stay alive.
Method 2: getCompletedTaskCount
Continuously compare the total planned task count with the completed task count; when they match, all tasks are done. This does not require shutting down the pool.
private static void isCompletedByTaskCount(ThreadPoolExecutor threadPool) {
while (threadPool.getTaskCount() != threadPool.getCompletedTaskCount()) {
// busy‑wait
}
}Pros: No need to close the pool.
Cons: Both counts are approximate because the pool state can change during the check.
Method 3: CountDownLatch
Create a CountDownLatch with a count equal to the number of tasks. Each task calls countDown() when finished, and the main thread waits on await() until the count reaches zero.
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
int taskCount = 5;
CountDownLatch latch = new CountDownLatch(taskCount);
for (int i = 0; i < taskCount; i++) {
final int idx = i;
threadPool.submit(() -> {
try {
int sleep = new Random().nextInt(5);
TimeUnit.SECONDS.sleep(sleep);
} catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(String.format("Task %d completed", idx));
latch.countDown();
});
}
latch.await();
System.out.println("ThreadPool tasks completed!");
}Pros: Elegant, does not require shutting down the pool, and is widely used.
Cons: The latch can be used only once; a new latch is needed for another round.
Method 4: CyclicBarrier
Similar to CountDownLatch but reusable. A CyclicBarrier is created with the task count and an optional barrier action that runs when the count reaches zero. Each task calls await() after finishing.
public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 20, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>(1024));
int taskCount = 5;
CyclicBarrier barrier = new CyclicBarrier(taskCount, () -> {
System.out.println("All tasks have finished!");
});
for (int i = 0; i < taskCount; i++) {
final int idx = i;
threadPool.submit(() -> {
try {
int sleep = new Random().nextInt(5);
TimeUnit.SECONDS.sleep(sleep);
System.out.println(String.format("Task %d completed", idx));
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); }
});
}
}Pros: Reusable after calling reset() .
Cons: More complex to use and understand compared with CountDownLatch .
Summary
The four approaches each have trade‑offs: isTerminated requires pool shutdown; getCompletedTaskCount gives approximate results; CountDownLatch is simple and common but single‑use; CyclicBarrier is reusable but more intricate. Choose the method that best fits your project's lifecycle and complexity requirements.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.