侦探大挑战
128.12M · 2026-04-22
在后端开发中,线程池是应对高并发、保障系统稳定性的“护城河”。然而,如果不清楚其内部的调度逻辑,盲目调参,线程池反而可能成为导致内存溢出 (OOM) 或响应超时的“罪魁祸首”。
今天,我们就从 ThreadPoolExecutor 的底层源码出发,一步步揭开线程池运行机制与拒绝策略的神秘面纱。
直接使用 new Thread().start() 存在三大致命问题:
线程池通过 “池化技术” 解决了这些问题,实现了线程的复用与任务的有序调度。
在 ThreadPoolExecutor 内部,仅用一个 AtomicInteger 变量 ctl 就管理了两个核心指标:
通过位运算(如 ctlOf, runStateOf, workerCountOf),线程池能以极高的效率在一个原子操作内同时由于更新状态和计数,保证了并发下的准确性。
当一个任务通过 execute(Runnable) 提交时,线程池会按照以下顺序进行“防御式”处理:
corePoolSize,则直接创建一个新线程来执行任务。workQueue 中排队等待。maximumPoolSize,则创建一个非核心线程(临时工)来执行。严禁使用 Executors.newFixedThreadPool 等方法,因为它们默认使用的 LinkedBlockingQueue 长度为 Integer.MAX_VALUE,极易导致 OOM。
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 企业级自定义线程池示例
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
// 自定义线程工厂,方便监控和排查
ThreadFactory threadFactory = new ThreadFactory() {
private final AtomicInteger count = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
return new Thread(r, "business-pool-" + count.getAndIncrement());
}
};
// 创建自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程 2
5, // 最大线程 5
60, // 非核心存活 60s
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3), // 有界队列 3
threadFactory,
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:由调用者线程直接执行
);
// 提交 10 个任务演示流程
for (int i = 0; i < 10; i++) {
executor.execute(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 正在处理任务...");
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
executor.shutdown();
}
}
纠正:这是最常见的理解错误。任务必须先去队列排队。只有当 队列也塞不下了,才会去尝试开启非核心线程。
纠正:JVM 预置了四种策略:
RejectedExecutionException。核数 / (1 - 阻塞系数),阻塞系数通常在 0.8~0.9 之间。不要直接干掉进程。使用 shutdown()(不再接收新任务,执行完队列里的)或 shutdownNow()(尝试中断所有并返回未执行任务),并配合 awaitTermination 确保任务平滑结束。
线程池的本质是 “缓冲与解耦”。它通过 ctl 进行高效的状态控制,通过三级递进的任务处理逻辑实现流量削峰。作为后端作者,我建议你始终坚持 “显式配置、有界队列、自定义命名” 的原则,这才是编写工业级高并发代码的底气所在。