加载中...
线程池是后端开发中提升性能的利器,但如果用错,它也可能成为耗尽资源的元凶。
在高并发场景下,直接new Thread()不仅会带来巨大的内存与CPU切换开销,还可能导致系统无限制创建线程而崩溃。线程池通过复用线程、控制最大并发数,帮助我们优雅地管理线程资源。
然而,很多同学图方便,随手就是一个 Executors.newFixedThreadPool(10),这很可能为线上故障埋下隐患。今天我们就来聊聊如何正确地使用 Java 线程池。
Java 的 Executors 类提供了快速创建线程池的工厂方法,但它们各有陷阱:
它们内部使用无界的 LinkedBlockingQueue(长度为 Integer.MAX_VALUE)。
// 源码:队列无界
new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
它允许创建无限数量的线程(最大线程数为 Integer.MAX_VALUE)。
// 源码:最大线程数无界
new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
同样拥有最大线程数为 Integer.MAX_VALUE 的风险。
结论:在生产环境,请手动通过 ThreadPoolExecutor 构造函数来创建线程池,这样才能精确掌控资源。
ThreadPoolExecutor 共有7个核心参数:
import java.util.concurrent.*;
import com.google.common.util.concurrent.ThreadFactoryBuilder; // Guava 工具
public class ThreadPoolDemo {
public static void main(String[] args) {
// 核心线程数:与CPU密集型/IO密集型任务相关
int corePoolSize = 10;
int maxPoolSize = 20;
long keepAliveTime = 60L;
// 有界队列,容量 100
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(100);
// 自定义线程工厂,命名有意义,方便排查问题
ThreadFactory threadFactory = new ThreadFactoryBuilder()
.setNameFormat("biz-pool-%d")
.setDaemon(false)
.build();
// 拒绝策略:CallerRunsPolicy 让提交任务的线程自行执行,减缓提交速度
RejectedExecutionHandler handler = new ThreadPoolExecutor.CallerRunsPolicy();
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize,
maxPoolSize,
keepAliveTime,
TimeUnit.SECONDS,
workQueue,
threadFactory,
handler
);
( ; i < ; i++) {
i;
executor.execute(() -> {
System.out.println(Thread.currentThread().getName() + + taskId);
{
Thread.sleep();
} (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
Runtime.getRuntime().addShutdownHook( (() -> {
System.out.println();
executor.shutdown();
{
(!executor.awaitTermination(, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
System.out.println();
}));
}
}
ArrayBlockingQueue 或 LinkedBlockingQueue 指定容量,防止无限制膨胀。ThreadFactoryBuilder 或自定义 ThreadFactory,在日志中能清晰分辨业务线程。CallerRunsPolicy(让提交线程自己执行)做自然背压,或 AbortPolicy(抛异常,由上层处理),慎用 DiscardPolicy(静默丢弃)和 DiscardOldestPolicy。一个简单但不绝对的经验公式:
核心数 + 1核心数 * 2 或 核心数 / (1 - 阻塞系数)(阻塞系数通常取 0.8~0.9 表示任务等待 I/O 的时间比例)实际调优需结合监控(如线程活跃数、队列积压、任务等待时间)进行压测调整。
线程池不仅需要创建得当,运行时也要“看得见”。可以通过 ThreadPoolExecutor 自带的 getter 方法暴露指标:
// 定期打印或上报到监控系统
System.out.println("活跃线程: " + executor.getActiveCount());
System.out.println("队列积压: " + executor.getQueue().size());
System.out.println("已完成任务: " + executor.getCompletedTaskCount());
结合 Spring Boot Actuator、Micrometer 或自定义端点,可以实时查看线程池健康状态,防止堆积雪崩。
线程池是并发编程的基石,但它并非“银弹”。远离 Executors 快捷陷阱,坚持手动声明参数、使用有界队列、自定义命名、配置合理拒绝策略,并在生命周期中妥善管理——你的应用才能真正做到高性能与高稳定兼得。