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
CyclicBarrierMethod 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.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
