引言
在多线程编程中,线程池是一种非常重要的资源管理工具。合理使用线程池可以显著提高系统性能,避免频繁创建和销毁线程带来的开销。今天,我将为大家深入分析一个实用的ThreadPoolManager
实现,它来自com.kingdee.eas.util
包,是一个优秀的线程池管理工具类。
核心设计解析
1. 单例模式实现
private static final ThreadPoolManager INSTANCE = new ThreadPoolManager();
public static ThreadPoolManager getInstance() {
return INSTANCE;
}
ThreadPoolManager
采用饿汉式单例模式,确保全局只有一个线程池实例。这种设计避免了重复创建线程池带来的资源浪费,也便于统一管理。
2. 自动计算线程池大小
static {
int cpus = Runtime.getRuntime().availableProcessors();
if (cpus <= 0) {
logger.error("无法获取 CPU 核心数,默认使用 2");
cpus = 2;
}
AVAILABLE_PROCESSORS = cpus;
}
private ThreadPoolManager() {
int poolSize = AVAILABLE_PROCESSORS * 2;
poolSize = Math.max(2, poolSize); // 至少保留 2个线程
this.executor = Executors.newFixedThreadPool(poolSize);
registerShutdownHook();
}
这段代码展示了几个优秀实践:
根据CPU核心数动态计算线程池大小(核心数×2)
设置最小线程数为2,确保低配机器也能运行
使用静态初始化块预先计算CPU核心数,避免重复计算
3. 优雅的关闭机制
private void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
logger.error("【Shutdown Hook】开始关闭线程池...");
ThreadPoolManager.this.shutdown();
logger.error("【Shutdown Hook】线程池已关闭");
}
}));
}
public void shutdown() {
if (!executor.isShutdown()) {
executor.shutdown(); // 不再接受新任务
try {
if (!executor.awaitTermination(800, java.util.concurrent.TimeUnit.MILLISECONDS)) {
logger.error("线程池未完全关闭,尝试强制关闭...");
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
关闭机制的设计亮点:
注册JVM关闭钩子,确保应用退出时自动关闭线程池
分阶段关闭:先尝试优雅关闭,超时后强制关闭
正确处理中断异常,恢复中断状态
使用示例
使用这个线程池管理器非常简单:
// 提交任务
ThreadPoolManager.getInstance().submit(() -> {
// 你的任务代码
});
// 手动关闭(通常不需要,因为有shutdown hook)
// ThreadPoolManager.getInstance().shutdown();
最佳实践建议
任务设计:确保提交的任务是线程安全的,避免共享可变状态
异常处理:在任务内部处理好异常,避免任务因异常而终止
监控:可以扩展此类,添加监控线程池状态的功能
动态调整:对于更复杂的场景,可以考虑使用
ThreadPoolExecutor
直接创建线程池,以便动态调整参数
可能的改进方向
配置化:将线程池大小等参数改为可配置的,而不是硬编码
多样化线程池:支持创建不同类型的线程池(如缓存线程池、定时线程池等)
监控扩展:添加线程池状态监控和报警功能
任务队列限制:固定线程池默认使用无界队列,可以考虑改为有界队列避免OOM
总结
这个ThreadPoolManager
实现简洁而实用,涵盖了线程池管理的核心关注点:初始化、任务提交和优雅关闭。特别适合作为中小型应用的默认线程池管理方案。通过学习这个实现,我们可以深入理解Java线程池的最佳实践。
完整代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.apache.log4j.Logger;
public class ThreadPoolManager {
private static final Logger logger = Logger.getLogger("com.kingdee.eas.util.ThreadPoolManager");
private static final ThreadPoolManager INSTANCE = new ThreadPoolManager();
private final ExecutorService executor;
private static final int AVAILABLE_PROCESSORS;
static {
int cpus = Runtime.getRuntime().availableProcessors();
if (cpus <= 0) {
logger.error("无法获取 CPU 核心数,默认使用 2");
cpus = 2;
}
AVAILABLE_PROCESSORS = cpus;
}
// 私有构造函数
private ThreadPoolManager() {
// 根据当前CPU核心数,创建合适的核心线程数
int poolSize = AVAILABLE_PROCESSORS * 2;
poolSize = Math.max(2, poolSize); // 至少保留 2个线程
this.executor = Executors.newFixedThreadPool(poolSize);
registerShutdownHook();
}
public static ThreadPoolManager getInstance() {
return INSTANCE;
}
public void submit(Runnable task) {
executor.submit(task);
}
// 注册 JVM 关闭钩子,
private void registerShutdownHook() {
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
logger.error("【Shutdown Hook】开始关闭线程池...");
ThreadPoolManager.this.shutdown(); // 调用关闭方法
logger.error("【Shutdown Hook】线程池已关闭");
}
}));
}
/**
* 关闭线程池(建议在应用退出前调用)
*/
public void shutdown() {
if (!executor.isShutdown()) {
executor.shutdown(); // 不再接受新任务
try {
// 最多等待一段时间让任务完成
if (!executor.awaitTermination(800, java.util.concurrent.TimeUnit.MILLISECONDS)) {
logger.error("线程池未完全关闭,尝试强制关闭...");
// 强制中断正在执行的任务
executor.shutdownNow();
}
} catch (InterruptedException e) {
// 被中断时恢复中断状态
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
}