LESSON 9.3
Thread pool and futures/promises
Manage concurrency without thread-creation overhead by reusing pooled worker threads, and coordinate asynchronous completion with Futures and Promises.
6 min read
Spawning a new thread or unbounded Promise chain for every incoming request causes severe resource exhaustion. In multi-threaded systems like Java, spawning 50,000 OS threads crashes the JVM with OutOfMemoryError: unable to create native thread. In Node.js, firing 100,000 concurrent database queries simultaneously exhausts connection pools and floods network sockets.
The problem
// Thread explosion anti-pattern:
void handleRequests(List<Request> requests) {
for (var req : requests) {
// Unbounded thread allocation: 10,000 requests = 10 GB stack memory!
new Thread(() -> process(req)).start();
}
}The pattern
The Thread Pool pattern maintains a fixed collection of idle worker threads and a task queue. When a client submits a task, the task is enqueued. Available worker threads pull tasks from the queue, execute them, and return to the pool for reuse.
A Future / Promise represents the eventual completion (or failure) of the submitted asynchronous task, allowing clients to chain transformations (then(), thenApply()) or await results.
The intent: Amortize thread creation costs, cap concurrency, and manage asynchronous task completion safely.
In code
When to use it, and when not to
Use Thread Pools and Futures when:
- You have numerous short-lived asynchronous tasks that would otherwise incur massive thread setup/teardown costs.
- You must bound concurrency to prevent exhausting backend resources (database connections, network sockets, file descriptors).
- You need to track task completion, gather results, or handle timeouts.
Don't use Thread Pools when:
- Tasks are long-running, persistent services (e.g. background daemons listening on a socket): dedicate a specific long-lived thread instead.
Where you have already seen it
In Java, java.util.concurrent.Executors (newFixedThreadPool, newWorkStealingPool) is the industry standard. In Node.js, p-limit and p-queue provide promise pool concurrency limiting. Web servers like Tomcat or Jetty use thread pools to handle incoming HTTP requests up to configured thread limits.
Related patterns
- Thread Pool and Producer-Consumer: Thread pools rely on a Producer-Consumer work queue internally.
- Thread Pool and Object Pool: A Thread Pool is a specialized Object Pool where the pooled objects are active threads.
Try it
Cooperative Async Task Group LLD
Implement a task pool that gathers results across multiple concurrent tasks and cancels remaining work upon first failure.