这篇不空谈理论,直接给一套业务里能用的参数配置和代码模板。
先给结论
线程池配置最容易出问题的地方就三个:
- 队列无界,任务堆到 OOM
- 最大线程数乱设,把机器打满
- 拒绝策略没兜底,线上直接丢任务
下面我们用一套可落地模板解决它。
一、先准备一个"可观测"的线程池工厂
java
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
public final class BizThreadPoolFactory {
private BizThreadPoolFactory() {}
public static ThreadPoolExecutor newIoPool(String name, int core, int max, int queueSize) {
// 线程命名,线上排查时能直接看出业务来源
ThreadFactory threadFactory = new ThreadFactory() {
private final AtomicInteger idx = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, name + "-" + idx.getAndIncrement());
t.setDaemon(false);
return t;
}
};
// 有界队列:防止任务无限堆积
BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(queueSize);
// CallerRunsPolicy:降级回调用线程执行,给上游施压,而不是直接丢任务
RejectedExecutionHandler rejectHandler = new ThreadPoolExecutor.CallerRunsPolicy();
return new ThreadPoolExecutor(
core,
max,
60L,
TimeUnit.SECONDS,
queue,
threadFactory,
rejectHandler
);
}
}
二、参数怎么给
1) CPU 密集型任务
java
int cpu = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor pool = BizThreadPoolFactory.newIoPool(
"cpu-task",
cpu,
cpu + 1,
200
);
适合:压缩、加密、规则计算。
2) IO 密集型任务
java
int cpu = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor pool = BizThreadPoolFactory.newIoPool(
"io-task",
cpu * 2,
cpu * 4,
1000
);
适合:RPC、数据库、磁盘读写。
三、业务代码里怎么用
java
import java.util.concurrent.*;
public class OrderQueryService {
private final ThreadPoolExecutor ioPool = BizThreadPoolFactory.newIoPool("order-io", 16, 32, 1000);
public String queryWithTimeout(String orderId) {
Future<String> future = ioPool.submit(() -> remoteCall(orderId));
try {
// 超时必须有,不然线程会一直卡住
return future.get(800, TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
future.cancel(true);
return "timeout-fallback";
} catch (Exception e) {
return "error-fallback";
}
}
private String remoteCall(String orderId) {
// 模拟远程调用
return "ok-" + orderId;
}
}
四、监控必须加
java
public final class ThreadPoolMetrics {
public static String snapshot(ThreadPoolExecutor pool) {
return "poolSize=" + pool.getPoolSize()
+ ", active=" + pool.getActiveCount()
+ ", queue=" + pool.getQueue().size()
+ ", completed=" + pool.getCompletedTaskCount()
+ ", taskCount=" + pool.getTaskCount();
}
}
定时打印或上报这几个值,线上问题会好排查很多。
五、优雅关闭
java
public static void shutdownGracefully(ThreadPoolExecutor pool) {
pool.shutdown(); // 拒绝新任务
try {
if (!pool.awaitTermination(30, TimeUnit.SECONDS)) {
pool.shutdownNow(); // 超时后强制中断
}
} catch (InterruptedException e) {
pool.shutdownNow();
Thread.currentThread().interrupt();
}
}
六、一个完整配置建议
java
int cpu = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor bizPool = BizThreadPoolFactory.newIoPool(
"biz-main",
Math.max(8, cpu * 2), // core
Math.max(16, cpu * 4), // max
2000 // queueSize
);
这套适合大多数"Web 请求 + 下游 IO"场景。
最后总结
线程池真正的稳定性来自四件事:
- 有界队列
- 合理的 core/max
- 可控的拒绝策略
- 持续监控和超时兜底
把这四件事做好,线程池基本不会翻车。