2004年J2SE 5.0发布时,Java引入了一个改变多线程编程格局的工具包——java.util.concurrent。其中Executor框架的诞生,直接解决了传统Thread类管理的三大痛点:
java复制while(true) new Thread(() -> {
try { Thread.sleep(100000); }
catch (InterruptedException e) {}
}).start();
我在电商系统压测时就遇到过典型场景:当并发用户达到2000时,用原生Thread创建的订单服务直接CPU 100%,而改用ThreadPoolExecutor后,相同压力下CPU利用率稳定在65%左右。
code复制Executor (接口)
└── ExecutorService (接口)
├── AbstractExecutorService (抽象类)
│ └── ThreadPoolExecutor (实现类)
└── ScheduledExecutorService (接口)
└── ScheduledThreadPoolExecutor (实现类)
java复制public ThreadPoolExecutor(
int corePoolSize, // 常驻核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
参数设置黄金法则:
警告:不要使用无界队列(如LinkedBlockingQueue),这会导致OOM。推荐使用ArrayBlockingQueue并设置合理容量
| 策略类 | 行为 | 适用场景 |
|---|---|---|
| AbortPolicy | 直接抛出RejectedExecutionException | 需要明确知道任务被拒绝 |
| CallerRunsPolicy | 用调用者线程执行任务 | 不希望丢失任务的慢速系统 |
| DiscardPolicy | 静默丢弃任务 | 可容忍丢失的监控类任务 |
| DiscardOldestPolicy | 丢弃队列最老任务 | 时效性强的场景 |
java复制ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(5);
// 定时打印线程池状态
ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();
monitor.scheduleAtFixedRate(() -> {
System.out.println("活跃线程数: " + executor.getActiveCount());
System.out.println("已完成任务数: " + executor.getCompletedTaskCount());
System.out.println("队列大小: " + executor.getQueue().size());
}, 0, 1, TimeUnit.SECONDS);
监控指标黄金三角:
java复制executor.shutdown();
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow();
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
System.err.println("线程池未正常关闭");
}
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
通过JMH基准测试比较不同线程数的吞吐量:
code复制Benchmark (threadCount) Mode Cnt Score Error Units
ThreadPoolBenchmark.test 1 thrpt 5 452.234 ± 6.234 ops/s
ThreadPoolBenchmark.test 4 thrpt 5 983.562 ± 8.762 ops/s
ThreadPoolBenchmark.test 8 thrpt 5 1204.873 ± 9.873 ops/s
ThreadPoolBenchmark.test 16 thrpt 5 987.263 ± 7.263 ops/s
结论:当线程数超过CPU核心数(测试机为8核)时,性能不升反降
在支付系统中,我采用分层线程池设计:
这种隔离避免了慢日志拖垮核心交易,实测使99线从1200ms降到350ms
场景1:任务间相互依赖
java复制ExecutorService pool = Executors.newFixedThreadPool(2);
Future<String> task1 = pool.submit(() -> {
Future<String> task2 = pool.submit(() -> "Hello");
return task2.get() + " World"; // 死锁点
});
System.out.println(task1.get());
解决方案:使用ForkJoinPool或增加线程数
某次线上OOM后发现是因为:
根治方案:
java复制try {
threadLocal.set(data);
// ...业务逻辑
} finally {
threadLocal.remove(); // 必须清理
}
java复制CompletableFuture.supplyAsync(() -> queryFromDB(), dbPool)
.thenApplyAsync(data -> process(data), computePool)
.thenAcceptAsync(result -> sendNotification(result), ioPool)
.exceptionally(ex -> {
log.error("流程异常", ex);
return null;
});
优势:
Java 19引入的虚拟线程(协程)用法:
java复制try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
IntStream.range(0, 10_000).forEach(i -> {
executor.submit(() -> {
Thread.sleep(Duration.ofSeconds(1));
return i;
});
});
}
测试创建10k虚拟线程仅需1.3秒,而原生线程需要分钟级