ExecutorService 与线程池类型
创建固定、缓存、单线程和定时线程池,并理解各自的适用场景
ExecutorService 与线程池类型 是 CoddyKit 上的免费 Java Academy 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Java Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Java Academy 课程共包含 4 节课。
为什么使用线程池
为每个任务创建新的 Thread 的开销很大——线程的创建和销毁都会产生额外开销。线程池会重复使用一组固定的线程来执行多个任务,从而减少开销并控制资源消耗。
import java.util.concurrent.*;
// Bad practice: new thread per task
new Thread(() -> System.out.println("task")).start();
// Better: reuse threads from a pool
ExecutorService pool = Executors.newFixedThreadPool(4);
pool.submit(() -> System.out.println("task from pool"));
pool.shutdown();newFixedThreadPool
创建一个恰好包含 N 个线程的线程池。额外任务会排队,直到有线程可用。当您知道所需的并行度时,这种线程池很适用。
ExecutorService pool = Executors.newFixedThreadPool(4);
for (int i = 0; i < 10; i++) {
final int taskId = i;
pool.submit(() -> {
System.out.println("Task " + taskId + " on " + Thread.currentThread().getName());
});
}
pool.shutdown();
pool.awaitTermination(10, TimeUnit.SECONDS);newCachedThreadPool
按需创建线程并重复使用空闲线程。空闲 60 秒的线程会被终止。它适合大量短时任务;但对于长时间运行或数量不受限制的任务存在风险,因为可能耗尽内存。
ExecutorService cached = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
cached.submit(() -> {
// quick task
double result = Math.sqrt(Math.random());
});
}
cached.shutdown();newSingleThreadExecutor
一个恰好包含一个线程的线程池。任务按照提交顺序依次执行。它适合将对共享资源的访问串行化。
ExecutorService single = Executors.newSingleThreadExecutor();
single.submit(() -> System.out.println("Task 1"));
single.submit(() -> System.out.println("Task 2"));
single.submit(() -> System.out.println("Task 3"));
// Always prints: Task 1, Task 2, Task 3 (sequential)
single.shutdown();newScheduledThreadPool
支持定时任务和重复任务。可用于轮询、重试和后台维护。
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
// One-shot delay
scheduler.schedule(() -> System.out.println("Delayed!"),
3, TimeUnit.SECONDS);
// Fixed rate (every 2 seconds)
scheduler.scheduleAtFixedRate(
() -> System.out.println("Tick"),
0, 2, TimeUnit.SECONDS);
// Run later: scheduler.shutdown();VirtualThreadExecutor(Java 21 及更高版本)
Java 21 引入了虚拟线程——它们轻量且创建成本低。对于 I/O 密集型工作负载,请使用 Executors.newVirtualThreadPerTaskExecutor():
// Java 21+
try (var exec = Executors.newVirtualThreadPerTaskExecutor()) {
for (int i = 0; i < 10_000; i++) {
final int id = i;
exec.submit(() -> {
Thread.sleep(100); // blocks without tying up OS thread
System.out.println("vthread " + id);
});
}
} // auto-shutdown选择合适的线程池
指导原则:
- CPU 密集型任务:使用固定线程池,N = CPU 核心数
- 输入/输出密集型任务:使用缓存线程池或虚拟线程
- 顺序任务:使用单线程执行器
- 定时/重复任务:使用定时线程池
int cores = Runtime.getRuntime().availableProcessors();
ExecutorService cpuPool = Executors.newFixedThreadPool(cores);
System.out.println("Pool size: " + cores);自定义 ThreadPoolExecutor
如果需要精细控制,请直接创建 ThreadPoolExecutor:
ThreadPoolExecutor tpe = new ThreadPoolExecutor(
2, // corePoolSize
10, // maximumPoolSize
60L, // keepAliveTime
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100), // work queue
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() // rejection policy
);
tpe.submit(() -> System.out.println("Custom pool task"));
tpe.shutdown();优雅关闭
务必关闭 ExecutorService 以释放线程。shutdown() 会停止接受新任务,但会完成队列中的任务。shutdownNow() 会尝试取消正在运行的任务。
ExecutorService pool = Executors.newFixedThreadPool(4);
// ... submit tasks ...
pool.shutdown(); // no new tasks
try {
if (!pool.awaitTermination(30, TimeUnit.SECONDS)) {
pool.shutdownNow(); // force cancel
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}ExecutorService 作为 AutoCloseable(Java 19 及更高版本)
在 Java 19 及更高版本中,ExecutorService 实现了 AutoCloseable。请使用带资源的 try 语句自动关闭:
// Java 19+ (preview), Java 21 stable
try (ExecutorService pool = Executors.newFixedThreadPool(4)) {
pool.submit(() -> System.out.println("task"));
} // pool.close() called: waits for tasks then shuts down拒绝策略
当线程池达到容量上限(所有线程都在工作且队列已满)时,拒绝策略会处理溢出的任务:
- AbortPolicy(默认):抛出
RejectedExecutionException - CallerRunsPolicy:由调用线程执行任务
- DiscardPolicy:静默丢弃任务
- DiscardOldestPolicy:丢弃队列中最早的任务
快速检查
您有一个执行大量计算的 CPU 密集型任务。固定线程池应包含多少个线程?
回顾:ExecutorService 与线程池
要点:
- 固定线程池:固定数量的 N 个线程;额外任务排队
- 缓存线程池:按需扩展;空闲线程在 60 秒后过期
- 单线程:保证串行执行顺序
- 定时线程池:延迟任务和重复任务
- 务必调用 shutdown();使用 awaitTermination() 实现干净退出
常见问题解答
「ExecutorService 与线程池类型」课时是免费的吗?
是的 — 「ExecutorService 与线程池类型」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Java Academy 课程的其余内容,请升级到 CoddyKit PRO。 Java Academy 课程共包含 4 节课。
「ExecutorService 与线程池类型」这节课中我会学到什么?
创建固定、缓存、单线程和定时线程池,并理解各自的适用场景 你通过在浏览器中直接运行的动手代码来练习 Java Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Java Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Java Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「ExecutorService 与线程池类型」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Java Academy 课中编写并运行代码吗?
能。每节 Java Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- ExecutorService 与线程池类型
- 提交任务:Runnable 与 Callable
- Future 与错误处理
- 使用 ScheduledExecutorService 执行重复任务