← Back to Advanced Java Course | Chapter 2: Multithreading | Lesson 8 of 8

Java Thread Pools

Introduction to Thread Pools

Creating a brand-new thread for every task adds real memory and scheduling overhead, and doing that frequently under load can degrade an application's performance and even destabilize it. A thread pool solves this by maintaining a queue of reusable worker threads that tasks are handed off to, rather than spawning fresh threads on demand.

Example: Introduction to Thread Pools

java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
	public static void main(String[] args) {
		ExecutorService pool = Executors.newFixedThreadPool(2);
		pool.submit(() -> System.out.println("Reused worker thread runs this task"));
		pool.shutdown();
	}
}

Cached Thread Pools

A cached thread pool dynamically resizes itself based on current load: it creates new threads as tasks arrive faster than existing threads can process them, and it retires threads that have sat idle for a while. This makes it well suited to workloads with many short-lived, bursty tasks.

Example: Cached Thread Pools

java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
	public static void main(String[] args) {
		ExecutorService pool = Executors.newCachedThreadPool();
		for (int i = 0; i < 3; i++) {
			pool.submit(() -> System.out.println("Short-lived task"));
		}
		pool.shutdown();
	}
}

Scheduled Thread Pools

A scheduled thread pool lets you schedule tasks to run once after a specified delay, or repeatedly at a fixed rate or fixed interval, without you having to manage timers yourself. This is the standard building block for background jobs like periodic cache refreshes or health checks.

Example: Scheduled Thread Pools

java
import java.util.concurrent.*;
public class Main {
	public static void main(String[] args) throws InterruptedException {
		ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
		scheduler.schedule(() -> System.out.println("Ran after delay"), 100, TimeUnit.MILLISECONDS);
		Thread.sleep(200);
		scheduler.shutdown();
	}
}

Single Thread Executor

A single-thread executor pool runs every submitted task sequentially on one dedicated background thread, guaranteeing tasks execute in the exact order they were submitted. This is useful whenever you need predictable, race-free ordering without paying for the complexity of explicit locking.

Example: Single Thread Executor

java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
	public static void main(String[] args) {
		ExecutorService pool = Executors.newSingleThreadExecutor();
		pool.submit(() -> System.out.println("First"));
		pool.submit(() -> System.out.println("Second")); // guaranteed to run after First
		pool.shutdown();
	}
}

Shutting Down Thread Pools

Always shut down thread pools you create once they're no longer needed, since their live worker threads will otherwise keep the JVM running and leak resources indefinitely. Use shutdown() to let already-running tasks finish gracefully, or shutdownNow() to force an immediate stop.

Example: Shutting Down Thread Pools

java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Main {
	public static void main(String[] args) {
		ExecutorService pool = Executors.newFixedThreadPool(2);
		pool.submit(() -> System.out.println("Task"));
		pool.shutdown(); // lets running tasks finish
		// pool.shutdownNow(); // would force an immediate stop instead
	}
}

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.