一、为什么需要线程池?
在大数据、高并发的时代背景下,Java线程池作为并发编程的利器,已成为开发者必须掌握的核心技能。让我们先看一组对比数据:
- 直接创建线程耗时:约0.5ms
- 线程池获取线程耗时:约0.01ms
- 系统默认最大线程数:约1万个(Linux系统)
通过线程池技术,我们可以实现线程的复用管理,有效避免频繁创建/销毁线程的系统开销,同时提供流量控制、任务队列管理等关键能力。
二、线程池核心架构解析
1. 线程池类关系图
2. 核心参数详解
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数(常驻线程)
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler) // 拒绝策略
三、线程池工作流程实战
1. 银行窗口模型
想象银行办理业务的场景:
- 核心窗口(corePoolSize)
- 临时窗口(maximumPoolSize - corePoolSize)
- 等候区(workQueue)
- 客满策略(handler)
2. 代码示例
public class ThreadPoolDemo {
public static void main(String[] args) {
// 创建自定义线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程2个
5, // 最大线程5个
60, TimeUnit.SECONDS, // 空闲线程存活时间
new ArrayBlockingQueue<>(10), // 容量10的队列
Executors.defaultThreadFactory(), // 默认线程工厂
new ThreadPoolExecutor.AbortPolicy()); // 拒绝策略
// 提交20个任务
for (int i = 0; i < 20; i++) {
final int taskId = i;
executor.execute(() -> {
System.out.println(Thread.currentThread().getName()
+ " 执行任务:" + taskId);
try {
Thread.sleep(1000); // 模拟任务执行
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
executor.shutdown(); // 优雅关闭
}
}
执行结果分析:
pool-1-thread-1 执行任务:0
pool-1-thread-2 执行任务:1
pool-1-thread-3 执行任务:11
pool-1-thread-4 执行任务:12
pool-1-thread-5 执行任务:13
(后续任务进入队列或被拒绝)
四、四大线程池类型对比
1. 创建方式对比
// 固定大小线程池
ExecutorService fixedPool = Executors.newFixedThreadPool(5);
// 单线程池
ExecutorService singlePool = Executors.newSingleThreadExecutor();
// 缓存线程池
ExecutorService cachedPool = Executors.newCachedThreadPool();
// 调度线程池
ScheduledExecutorService scheduledPool =
Executors.newScheduledThreadPool(3);
2. 内部实现差异
类型 | 核心线程数 | 最大线程数 | 队列类型 |
FixedThreadPool | 指定值 | 同核心数 | LinkedBlockingQueue |
CachedThreadPool | 0 | Integer.MAX | SynchronousQueue |
SingleThreadPool | 1 | 1 | LinkedBlockingQueue |
ScheduledPool | 指定值 | Integer.MAX | DelayedWorkQueue |
五、源码级深度解析
1. 核心执行流程(execute方法)
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// 阶段1:核心线程处理
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 阶段2:入队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 阶段3:创建非核心线程
else if (!addWorker(command, false))
reject(command); // 执行拒绝策略
}
2. Worker线程工作原理
每个Worker包含:
- Thread实例:实际执行线程
- Runnable task:初始任务
- 循环从队列获取任务执行
六、实战经验与避坑指南
1. 参数配置黄金法则
(1) CPU密集型:核心数 = CPU核数 + 1
(2) IO密集型:核心数 = CPU核数 * 2
(3) 队列选择:
- 快速响应:SynchronousQueue
- 流量削峰:LinkedBlockingQueue
- 延时任务:DelayedWorkQueue
2. 常见问题解决方案
场景1:任务堆积导致OOM
// 错误示范:使用无界队列
new ThreadPoolExecutor(n, n, 0, TimeUnit.SECONDS,
new LinkedBlockingQueue<>());
// 正确做法:使用有界队列+合适拒绝策略
new ThreadPoolExecutor(n, 2*n, 60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000),
new CustomRejectedPolicy());
场景2:线程泄露
// 必须调用shutdown
executor.shutdown();
// 或者使用Hook关闭
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}));
3. 监控技巧
自定义线程池监控:
public class MonitorThreadPool extends ThreadPoolExecutor {
// 重写钩子方法
@Override
protected void beforeExecute(Thread t, Runnable r) {
System.out.println("Task start: " + ((Task) r).getId());
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
System.out.println("Task complete: " + ((Task) r).getId());
}
// 自定义监控方法
public void printStats() {
System.out.println("Pool Size: " + this.getPoolSize());
System.out.println("Active Count: " + this.getActiveCount());
System.out.println("Queue Size: " + this.getQueue().size());
}
}
七、线程池性能优化
1. 并行处理优化示例
// 使用CompletableFuture实现并行计算
public class ParallelProcessor {
private final ExecutorService executor =
Executors.newWorkStealingPool();
public Result process(List<Task> tasks) {
List<CompletableFuture<PartialResult>> futures = tasks.stream()
.map(task -> CompletableFuture.supplyAsync(
() -> compute(task), executor))
.collect(Collectors.toList());
CompletableFuture<Void> allDone =
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
return allDone.thenApply(v ->
futures.stream()
.map(CompletableFuture::join)
.reduce(new Result(), this::merge))
.join();
}
}
2. 上下文传递方案
// 使用TransmittableThreadLocal解决线程池上下文传递
TransmittableThreadLocal<String> context = new TransmittableThreadLocal<>();
void executeTask() {
context.set("main-context");
executor.execute(TtlRunnable.get(() -> {
System.out.println("Get context: " + context.get());
}));
}
八、总结与展望
通过本文的深度解析,相信你已经掌握了:
- 线程池的底层实现原理
- 参数配置的黄金法则
- 常见问题的解决方案
- 性能优化的高级技巧
未来趋势建议关注:
- 虚拟线程(Project Loom)
- 响应式编程结合
- AI自动调参技术