How to wait for a number of threads to complete?
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
Waiting for several threads to finish is a synchronization problem, not just a looping problem. The simplest answer is often join, but once the code grows beyond a few manually created threads, higher-level coordination tools are usually better. The right choice depends on whether you are dealing with raw threads, a thread pool, or task abstractions that already model completion.
Use join for Manually Created Threads
If you explicitly created Thread objects, the direct way to wait is to start them and then join each one.
join blocks the calling thread until the target thread finishes. This is fine for a small number of explicitly managed threads.
Use CountDownLatch for Group Completion
When you want "wait until N workers finish" without joining each one directly, CountDownLatch is a better abstraction.
This pattern is useful when worker threads may be created in different places but one coordinator needs to wait for them all.
Prefer Executors and Futures in Real Applications
Raw threads are often the wrong level for application code. If work units come from a pool, use an executor and wait on Future results or use invokeAll.
invokeAll blocks until every submitted task completes, which makes it a strong fit for batch-style thread-pool work.
Handle Timeouts and Interruptions Intentionally
Waiting forever is often a bug. If worker completion matters operationally, use a timeout-aware wait.
With CountDownLatch:
With join:
Also remember that blocking waits can throw InterruptedException. Good code either propagates it or restores the interrupt status instead of swallowing it.
Choose the Highest-Level Abstraction You Can
As the design matures, the best answer is usually:
- '
joinfor a few raw threads' - '
CountDownLatchfor group completion' - executor and futures for pooled work
- task frameworks such as
CompletableFuturefor async composition
If you find yourself manually managing large numbers of threads, that is usually a signal that the program should move to a higher-level concurrency model.
Common Pitfalls
- Joining threads that were never started.
- Forgetting to count down the latch in a
finallyblock. - Waiting forever without a timeout in code that runs in production workflows.
- Managing many raw threads manually when an executor would be simpler and safer.
- Swallowing
InterruptedExceptionand losing the thread interruption signal.
Summary
- '
joinis the direct way to wait for explicitly created threads.' - '
CountDownLatchis better when you need to wait for a group of workers to finish.' - Executors and futures are usually the right abstraction for real application concurrency.
- Add timeouts when a hang would be operationally harmful.
- Prefer higher-level concurrency tools over manual thread management as the design grows.
Related reading
- How to wait for a stream to finish piping? Nodejs
- How to wait for all tasks in an ThreadPoolExecutor to finish without shutting down the Executor?
- How to wait for all threads to finish, using ExecutorService?
- How to wait for all threads to finish, using ExecutorService?
- How to wait for async actions inside AWS Lambda?
- How to wait for async method to complete?
- How to wait for async method to finish in C?
- How to wait for async mounted of Vue component to finish before continuing with testing
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.