Java线程池管理最佳实践(设计模式)

引言

在多线程编程中,线程池是一种非常重要的资源管理工具。合理使用线程池可以显著提高系统性能,避免频繁创建和销毁线程带来的开销。今天,我将为大家深入分析一个实用的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();
}

这段代码展示了几个优秀实践:

  1. 根据CPU核心数动态计算线程池大小(核心数×2)

  2. 设置最小线程数为2,确保低配机器也能运行

  3. 使用静态初始化块预先计算CPU核心数,避免重复计算

3. 优雅的关闭机制

private void registerShutdownHook() {Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {@Overridepublic 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();}}
}

关闭机制的设计亮点:

  1. 注册JVM关闭钩子,确保应用退出时自动关闭线程池

  2. 分阶段关闭:先尝试优雅关闭,超时后强制关闭

  3. 正确处理中断异常,恢复中断状态

使用示例

使用这个线程池管理器非常简单:

// 提交任务
ThreadPoolManager.getInstance().submit(() -> {// 你的任务代码
});// 手动关闭(通常不需要,因为有shutdown hook)
// ThreadPoolManager.getInstance().shutdown();

最佳实践建议

  1. 任务设计:确保提交的任务是线程安全的,避免共享可变状态

  2. 异常处理:在任务内部处理好异常,避免任务因异常而终止

  3. 监控:可以扩展此类,添加监控线程池状态的功能

  4. 动态调整:对于更复杂的场景,可以考虑使用ThreadPoolExecutor直接创建线程池,以便动态调整参数

可能的改进方向

  1. 配置化:将线程池大小等参数改为可配置的,而不是硬编码

  2. 多样化线程池:支持创建不同类型的线程池(如缓存线程池、定时线程池等)

  3. 监控扩展:添加线程池状态监控和报警功能

  4. 任务队列限制:固定线程池默认使用无界队列,可以考虑改为有界队列避免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() {@Overridepublic 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();}}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/news/907278.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

4.8.2 利用Spark SQL计算总分与平均分

在本次实战中&#xff0c;我们的目标是利用Spark SQL计算学生的总分与平均分。首先&#xff0c;我们准备了包含学生成绩的数据文件&#xff0c;并将其上传至HDFS。接着&#xff0c;通过Spark的交互式编程环境&#xff0c;我们读取了成绩文件并将其转换为结构化的DataFrame。然后…

HTML 文件路径完全指南:相对路径、绝对路径解析与引用技巧

一、为什么必须学会文件路径&#xff1f;—— 网页引用资源的 “地址规则” 在 HTML 中&#xff0c;引用图片、CSS、JS 等外部文件时&#xff0c;必须通过文件路径告诉浏览器资源的位置。路径错误会导致资源无法加载&#xff08;页面出现 broken image 图标或样式丢失&#xf…

keepalived两台设备同时出现VIP问题

目录 问题背景&#xff1a; 日志分析如下&#xff1a; 原因和解决方案总结&#xff1a; 问题背景&#xff1a; keepalived-master和keepalived-slave同时出现了VIP&#xff0c;出现了非对称路由和双主现象 日志分析如下&#xff1a; master能够接受到来自slave的通告消息…

【开源解析】基于PyQt5+Folium的谷歌地图应用开发:从入门到实战

&#x1f310;【开源解析】基于PyQt5Folium的谷歌地图应用开发&#xff1a;从入门到实战 &#x1f308; 个人主页&#xff1a;创客白泽 - CSDN博客 &#x1f525; 系列专栏&#xff1a;&#x1f40d;《Python开源项目实战》 &#x1f4a1; 热爱不止于代码&#xff0c;热情源自每…

篇章五 数据结构——链表(一)

目录 1.ArrayList的缺陷 2. 链表 2.1 链表的概念及结构 2.2 链表结构 1. 单向或者双向 2.带头或者不带头 3.循环或者非循环 2.3 链表的实现 1.完整代码 2.图解 3.显示方法 4.链表大小 5. 链表是否存在 key 值 6.头插法 7.尾插法 8.中间插入 9.删除key值节点 10.…

数据库相关面试

数据库相关面试 Mysql MySQL中的事务隔离级别及其特点 参考&#xff1a;事务的隔离级别&#xff1a;可重复读 未提交读(Read Uncommitted)&#xff1a;允许脏读&#xff0c;也就是可能读取到其他会话中未提交事务修改的数据 已提交读(Read Committed)&#xff1a;只能读取到…

基于Scrapy的天猫商品数据爬取与分析实战(含API签名破解与可视化)

基于Scrapy的天猫商品数据爬取与分析实战&#xff08;含API签名破解与可视化&#xff09; 本文以华为Mate 60 Pro为例&#xff0c;详细介绍如何使用Scrapy框架爬取天猫商品数据&#xff0c;涵盖API签名破解、反爬应对、数据存储及可视化全流程&#xff0c;适合爬虫进阶学习者实…

【C++进阶篇】哈希表的模拟实现(赋源码)

这里写目录标题 前言一. 开放地址法实现哈希表1.1 闭散列结构定义1.2 构造函数1.3 插入&#xff08;线性探测&#xff09;1.3.1 传统写法1.3.2 现代写法 1.4 查找1.5 删除 二. 链地址法实现哈希表&#xff08;哈希桶&#xff09;2.1 开散列结构定义2.2 构造函数2.3 插入2.4 查找…

07-后端Web实战(部门管理)

5. 修改部门 对于任何业务的修改功能来说&#xff0c;一般都会分为两步进行&#xff1a;查询回显、修改数据。 5.1 查询回显 5.1.1 需求 当我们点击 "编辑" 的时候&#xff0c;需要根据ID查询部门数据&#xff0c;然后用于页面回显展示。 5.1.2 接口描述 参照参照…

深度解析项目集方向或目标根本性转变的应对策略 —— 项目集管理实战指南

在复杂多变的商业环境中&#xff0c;项目集管理面临着重重挑战&#xff0c;而项目集方向或目标的根本性转变无疑是其中最具冲击力的问题之一。本文将深度剖析这一难题&#xff0c;为项目集管理从业者提供实用、新颖且富有价值的应对策略&#xff0c;助力大家在项目集管理的复杂…

JAVA面试复习知识点

面试中遇到的题目&#xff0c;记录复习&#xff08;持续更新&#xff09; Java基础 1.String的最大长度 https://www.cnblogs.com/wupeixuan/p/12187756.html 2.集合 Collection接口的实现&#xff1a; List接口&#xff1a;ArraryList、LinkedList、Vector Set接口&#xff1a…

【烧脑算法】定长滑动窗口:算法题中的“窗口”智慧

目录 引言 定长滑动窗口习题剖析 3439. 重新安排会议得到最多空余时间 I 2134. 最少交换次数来组合所有的 1 II 1297. 子串的最大出现次数 2653. 滑动子数组的美丽值 567. 字符串的排列 438. 找到字符串中所有字母异位词 30. 串联所有单词的子串 220. 存在重复元素 II…

JWT安全:接收无签名令牌.【签名算法设置为none绕过验证】

JWT安全&#xff1a;假密钥【签名随便写实现越权绕过.】 JSON Web 令牌 (JWT)是一种在系统之间发送加密签名 JSON 数据的标准化格式。理论上&#xff0c;它们可以包含任何类型的数据&#xff0c;但最常用于在身份验证、会话处理和访问控制机制中发送有关用户的信息(“声明”)。…

XGBoost与SHAP深度解析:从算法原理到实战价值

在机器学习领域&#xff0c;XGBoost以其卓越的性能长期占据Kaggle竞赛和工业界的主流地位&#xff0c;而SHAP&#xff08;SHapley Additive exPlanations&#xff09;则成为模型可解释性的标杆工具。本文将深度解析两者的技术内核&#xff0c;并通过实战案例揭示其结合应用的实…

Java SE Cloneable接口和深/浅拷贝

Java为我们提供了各种各样功能的接口&#xff0c;Clonable接口就是其中之一。 它通常配合Object类的 clone方法使用。这个方法可以为我们创建一个对象的拷贝&#xff0c;即复制一个对象。在进入本文的主要内容之前&#xff0c;先来对访问限定符 protected进行一个解剖。 1.再…

Python学习(3) ----- Python的函数定义及其使用

Python 中函数是组织好的、可重复使用的代码块&#xff0c;用于实现单一或相关联的功能。下面是函数定义和使用的完整说明&#xff1a; &#x1f4cc; 一、函数定义语法 def 函数名(参数1, 参数2默认值, *args, **kwargs):"""函数说明文档"""函…

vue2使用el-tree实现两棵树间节点的拖拽复制

原文链接&#xff1a;两棵el-tree的节点跨树拖拽实现 参照这篇文章&#xff0c;把它做成组件&#xff0c;新增左侧树&#xff08;可拖出&#xff09;被拖节点变灰提示&#xff1b; 拖拽中&#xff1a; 拖拽后&#xff1a; TreeDragComponent.vue <template><!-- …

智变与重构:AI 赋能基础教育教学的范式转型研究报告

一、研究背景与核心价值 &#xff08;一&#xff09;技术驱动下的教育转型浪潮 在全球数字化转型加速的背景下&#xff0c;人工智能作为核心技术力量&#xff0c;正重塑基础教育生态。据《人工智能赋能未来教育研究报告》指出&#xff0c;我国教育数字化战略行动已推动超 70…

Go语言中Print、Printf和Println的区别及使用场景详解

在Go语言的fmt包中&#xff0c;Print、Printf和Println是三个基础但功能各异的输出函数。本文将从多个维度进行详细对比分析&#xff0c;并给出具体的使用建议。 1. 核心区别深度解析 1.1. 函数签名与基本行为 func Print(a ...interface{}) (n int, err error) func Printf…

高端制造行业 VMware 替代案例合集:10+ 头部新能源、汽车、半导体制造商以国产虚拟化支持 MES、PLM 等核心应用系统

在“中国制造 2025”政策的推动下&#xff0c;国内的新能源、汽车制造、半导体、高端装备等高端制造产业迎来了蓬勃发展&#xff0c;成为全球制造业版图中举足轻重的力量。订单数量的激增与国产化转型的趋势&#xff0c;也为高端制造企业的 IT 基础设施带来了新的挑战&#xff…