当前位置: 首页 > news >正文

线程池10种常见坑

1. 直接使用 Executors 创建线程池

直接使用 Executors 提供的快捷方法:

ExecutorService executor = Executors.newFixedThreadPool(10);

问题

  • 无界队列newFixedThreadPool 使用的队列是 LinkedBlockingQueue,它是无界队列,任务堆积可能会导致内存溢出。

  • 线程无限增长newCachedThreadPool 会无限创建线程,在任务量激增时可能耗尽系统资源。

    // 内存溢出的风险
    ExecutorService executor = Executors.newFixedThreadPool(2);
    for (int i = 0; i < 1000000; i++) {executor.submit(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});
    }
    

    任务数远大于线程数,导致任务无限堆积在队列中,最终可能导致 OutOfMemoryError

解决

使用 ThreadPoolExecutor,并明确指定参数:

ThreadPoolExecutor executor = new ThreadPoolExecutor(2,4,60L,TimeUnit.SECONDS,new ArrayBlockingQueue<>(100), // 有界队列new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);

2. 错误配置线程数

很多人随意配置线程池参数,比如核心线程数 10,最大线程数 100,看起来没问题,但这可能导致性能问题或资源浪费。

// 错误配置导致的线程过载
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, // 核心线程数100, // 最大线程数60L,TimeUnit.SECONDS,new ArrayBlockingQueue<>(10)
);for (int i = 0; i < 1000; i++) {executor.submit(() -> {try {Thread.sleep(5000); // 模拟耗时任务} catch (InterruptedException e) {e.printStackTrace();}});
}

这种配置在任务激增时,会创建大量线程,系统资源被耗尽。

正确配置方式

根据任务类型选择合理的线程数:

  • CPU 密集型:线程数建议设置为 CPU 核心数 + 1
  • IO 密集型:线程数建议设置为 2 * CPU 核心数

示例:

int cpuCores = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor executor = new ThreadPoolExecutor(cpuCores + 1,cpuCores + 1,60L,TimeUnit.SECONDS,new ArrayBlockingQueue<>(50)
);

3. 忽略任务队列的选择

任务队列直接影响线程池的行为。如果选错队列类型,会带来很多隐患。

常见队列的坑

  • 无界队列:任务无限堆积。
  • 有界队列:队列满了会触发拒绝策略。
  • 优先级队列:容易导致高优先级任务频繁抢占低优先级任务。
// 任务堆积导致问题
ThreadPoolExecutor executor = new ThreadPoolExecutor(2,4,60L,TimeUnit.SECONDS,new LinkedBlockingQueue<>()
);for (int i = 0; i < 100000; i++) {executor.submit(() -> System.out.println(Thread.currentThread().getName()));
}

改进方法:用有界队列,避免任务无限堆积。

new ArrayBlockingQueue<>(100);

4. 忘记关闭线程池

有些小伙伴用完线程池后,忘记调用 shutdown(),导致程序无法正常退出。

// 线程池未关闭
ExecutorService executor = Executors.newFixedThreadPool(5);
executor.submit(() -> System.out.println("任务执行中..."));
// 线程池未关闭,程序一直运行

正确关闭方式

executor.shutdown();
try {if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {executor.shutdownNow();}
} catch (InterruptedException e) {executor.shutdownNow();
}

5. 忽略拒绝策略

当任务队列满时,线程池会触发拒绝策略,很多人不知道默认策略(AbortPolicy)会直接抛异常。

// 任务被拒绝
ThreadPoolExecutor executor = new ThreadPoolExecutor(1,1,60L,TimeUnit.SECONDS,new ArrayBlockingQueue<>(2),new ThreadPoolExecutor.AbortPolicy() // 默认策略
);for (int i = 0; i < 10; i++) {executor.submit(() -> System.out.println("任务"));
}

执行到第四个任务时会抛出 RejectedExecutionException

改进:选择合适的策略

  • CallerRunsPolicy:提交任务的线程自己执行。
  • DiscardPolicy:直接丢弃新任务。
  • DiscardOldestPolicy:丢弃最老的任务。

6. 任务中未处理异常

线程池中的任务抛出异常时,线程池不会直接抛出,导致很多问题被忽略。

// 异常被忽略
executor.submit(() -> {throw new RuntimeException("任务异常");
});

解决方法

  1. 捕获任务内部异常:

    executor.submit(() -> {try {throw new RuntimeException("任务异常");} catch (Exception e) {System.err.println("捕获异常:" + e.getMessage());}
    });
    
  2. 自定义线程工厂:

    ThreadFactory factory = r -> {Thread t = new Thread(r);t.setUncaughtExceptionHandler((thread, e) -> {System.err.println("线程异常:" + e.getMessage());});return t;
    };
    

7. 阻塞任务占用线程池

如果线程池中的任务是阻塞的(如文件读写、网络请求),核心线程会被占满,影响性能。

// 阻塞任务拖垮线程池
executor.submit(() -> {Thread.sleep(10000); // 模拟阻塞任务
});

改进方法

  • 减少任务的阻塞时间。
  • 增加核心线程数。
  • 使用异步非阻塞方式(如 NIO)。

8. 滥用线程池

线程池不是万能的,某些场景直接使用 new Thread() 更简单。

一个简单的短期任务:

// 过度使用线程池
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> System.out.println("执行任务"));
executor.shutdown();

这种情况下,用线程池反而复杂。

改进方式

new Thread(() -> System.out.println("执行任务")).start();

9. 未监控线程池状态

很多人用线程池后,不监控其状态,导致任务堆积、线程耗尽的问题被忽略。

// 监控线程池状态
System.out.println("核心线程数:" + executor.getCorePoolSize());
System.out.println("队列大小:" + executor.getQueue().size());
System.out.println("已完成任务数:" + executor.getCompletedTaskCount());

结合监控工具(如 JMX、Prometheus),实现实时监控。

10. 动态调整线程池参数

有些人在线程池设计时忽略了参数调整的必要性,导致后期性能优化困难。

// 动态调整核心线程数
executor.setCorePoolSize(20);
executor.setMaximumPoolSize(50);

实时调整线程池参数,能适应业务的动态变化。

相关文章:

线程池10种常见坑

1. 直接使用 Executors 创建线程池 直接使用 Executors 提供的快捷方法&#xff1a; ExecutorService executor Executors.newFixedThreadPool(10);问题 无界队列&#xff1a;newFixedThreadPool 使用的队列是 LinkedBlockingQueue&#xff0c;它是无界队列&#xff0c;任务…...

鸿蒙ArkTs如何实现pdf预览功能?

鸿蒙ArkTs如何实现pdf预览功能&#xff1f; 前言PDFKit运行示例代码报错真机运行先看效果一、预览本地pdf文件二、预览线上的pdf文件三、预览沙箱目录中pdf的文件(重点)效果中的整体代码总结 Harmony OS NEXT版本&#xff08;接口及解决方案兼容API12版本或以上版本) 前言 在开…...

KylinSP3 | 防火墙和麒麟安全增强设置KySec

一、系统防火墙原理 麒麟操作系统从V10版本开始&#xff0c;默认使用了Firewalld防火墙&#xff0c;Firewalld是能提供动态管理的防火墙&#xff0c;支持网络/防火墙区域&#xff0c;用于定义网络连接或接口的信任级别。支持IPv4和IPv6防火墙设置、以太网桥接和IP集。将运行时…...

【C++】面试常问八股

5、内存管理 野指针 野指针指的是未进行初始化或未清零的指针&#xff0c;不是NULL指针野指针产生原因及解决方案&#xff1a; 指针变量未初始化&#xff1a;指针变量定义时若未初始化&#xff0c;则其指向的地址是随机的&#xff0c;不为NULL&#xff1b;定义时初始化为NULL…...

vscode多文件编译构建(CMake)和调试C++

目录 1. CMake 基础构建工具及作用相关配置文件 2. 配置 tasks.json关键字段详细解释 3. 配置 launch.json关键字段详细解释 4. 配置 CMakeLists.txt关键部分详细解释 5. 构建和调试项目1. 仅构建项目1.1 任务执行顺序1.2 cmake 任务执行详情1.3 build 任务执行详情1.4 构建后的…...

使用Docker 部署 LNMP+Redis 环境

使用Docker 部署 LNMPRedis 环境 Docker 简介 Docker 是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何流行的 Linux 机器上&#xff0c;也可以实现虚拟化。容器是完全使用沙箱机制&#xff0c;相互…...

文件上传漏洞学习笔记

一、漏洞概述 定义 文件上传漏洞指未对用户上传的文件进行充分安全校验&#xff0c;导致攻击者可上传恶意文件&#xff08;如Webshell、木马&#xff09;&#xff0c;进而控制服务器或执行任意代码。 危害等级 ⚠️ 高危漏洞&#xff08;通常CVSS评分7.0&#xff09;&#xff…...

375_C++_cloud手机推送,添加人脸告警信息到任务队列中,UploadAlarmPush是典型的工厂模式应用,为什么使用工厂模式完成这部分代码

一:AlarmFaceInfo的应用 让我帮你解析这个lambda表达式的实现: // ...................... .h ...........................// struct RsMsgPushTask_S : public Task{AlarmType_E mainAlarmType;unsigned int subAlarmType;DateTime alarmTime...

Spring Boot 中的日志管理

一、日志框架选择 1. 主流框架对比 框架特点Spring Boot 默认支持Logback- 性能优异&#xff0c;Spring Boot 默认集成- 支持自动热更新配置文件✅ (默认)Log4j2- 异步日志性能更强- 支持插件扩展- 防范漏洞能力更好❌ (需手动配置)JUL (JDK自带)- 无需额外依赖- 功能简单&am…...

火绒终端安全管理系统V2.0网络防御功能介绍

火绒终端安全管理系统V2.0 【火绒企业版V2.0】网络防御功能包含网络入侵拦截、横向渗透防护、对外攻击检测、僵尸网络防护、Web服务保护、暴破攻击防护、远程登录防护、恶意网址拦截。火绒企业版V2.0的网络防御功能&#xff0c;多层次、多方位&#xff0c;守护用户终端安全。 …...

海康摄像头 + M7s(Monibuca) + FFmpeg + Python实现多个网络摄像头视频流推流

最近在研究流媒体服务器时&#xff0c;我注意到了一款开源软件——M7s。按照官网的指南部署完成后&#xff0c;我开始进行测试&#xff0c;发现单视频流推送非常顺利&#xff0c;没有任何问题。然而&#xff0c;当我尝试进行多视频流推送时&#xff0c;却发现网上的相关教程寥寥…...

抖音视频如何下载保存去水印

随着短视频平台的兴起&#xff0c;抖音作为国内最受欢迎的短视频平台之一&#xff0c;吸引了大量用户上传和观看各种创意视频。许多用户在浏览抖音视频时&#xff0c;往往会想要保存一些有趣或精彩的视频片段&#xff0c;但抖音视频通常会有水印&#xff0c;影响观看体验。为了…...

【鸿蒙开发】第三十九章 LazyForEach:数据懒加载

目录 1 背景 2 使用限制 键值生成规则 组件创建规则 首次渲染 非首次渲染 改变数据子属性 使用状态管理V2 拖拽排序 1 背景 LazyForEach从提供的数据源中按需迭代数据&#xff0c;并在每次迭代过程中创建相应的组件。当在滚动容器中使用了LazyForEach&#xff0c;框架…...

HTTP-

一.HTTP 1.什么是HTTP HTTP(超文本传输协议)是一种工作在应用层的协议.主要用于网站,就是浏览器和服务器之间的数据传输. 小知识:什么是超文本传输协议 文本:是字符串.(能在utf8/gbk码表上找到合法字符) 超文本:不仅可以传输字符串,也可以传输图片,html等 富文本:word文档 2.HT…...

创建型模式 - 原型模式 (Prototype Pattern)

创建型模式 -原型模式 (Prototype Pattern) 它允许通过复制现有对象来创建新对象&#xff0c;而无需知道对象的具体创建细节。在 Java 中&#xff0c;可以通过实现 Cloneable 接口和重写 clone() 方法来实现原型模式。 有深、浅两种克隆 类实现 Cloneable 接口就可以深克隆如果…...

Android 8.0 (API 26) 对广播机制做了哪些变化

大部分隐式广播无法通过静态注册接收&#xff0c;除了以下白名单广播&#xff1a; ACTION_BOOT_COMPLETED ACTION_TIMEZONE_CHANGED ACTION_LOCALE_CHANGED ACTION_MY_PACKAGE_REPLACED ACTION_PACKAGE_ADDED ACTION_PACKAGE_REMOVED 需要以动态注册方案替换&#xff1a; cl…...

Unity汽车笔记

汽车的移动和转向 我们知道&#xff0c;汽车的前进后退是变速运动。按w&#xff0c;汽车开始加速&#xff0c;到最大速度后保持匀速&#xff0c;松开w&#xff0c;汽车受到阻力加速。如果按s减速&#xff0c;则以更大的加速度减速。后退反之。 按A/D时前轮偏转。只有前进后退…...

html中rel、href、src、url的区别

1.url url&#xff08;统一资源定位符&#xff09;&#xff1a;是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示&#xff0c;是互联网上标准资源的地址。 2.href href&#xff1a;Hypertext Reference的缩写。 意思是超文本引用。 3.rel rel&#xff1a;relatio…...

【idea问题排查技巧】

以下是针对 IDEA 中 日志打标(动态标记) 和 全链路追踪 功能的分步详解,结合具体场景和操作截图说明,帮助快速掌握实战技巧。 一、动态日志打标:不修改代码输出关键信息 1. 断点日志打印(非侵入式打标) 场景:在调试时,需要临时查看某个变量的值,但不想修改代码添加…...

SQL: DDL,DML,DCL,DTL,TCL,

Structured Query Language&#xff0c;结构化查询语言, 是一种用于管理和操作关系数据库的标准编程语言。 sql的分类 DQL&#xff08;Data Query Language&#xff09;&#xff1a;数据查询语言 DDL&#xff08;Data Definition Language&#xff09;&#xff1a;数据定义语…...

springboot 百货中心供应链管理系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;百货中心供应链管理系统被用户普遍使用&#xff0c;为方…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

论文浅尝 | 基于判别指令微调生成式大语言模型的知识图谱补全方法(ISWC2024)

笔记整理&#xff1a;刘治强&#xff0c;浙江大学硕士生&#xff0c;研究方向为知识图谱表示学习&#xff0c;大语言模型 论文链接&#xff1a;http://arxiv.org/abs/2407.16127 发表会议&#xff1a;ISWC 2024 1. 动机 传统的知识图谱补全&#xff08;KGC&#xff09;模型通过…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践

6月5日&#xff0c;2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席&#xff0c;并作《智能体在安全领域的应用实践》主题演讲&#xff0c;分享了在智能体在安全领域的突破性实践。他指出&#xff0c;百度通过将安全能力…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

三分算法与DeepSeek辅助证明是单峰函数

前置 单峰函数有唯一的最大值&#xff0c;最大值左侧的数值严格单调递增&#xff0c;最大值右侧的数值严格单调递减。 单谷函数有唯一的最小值&#xff0c;最小值左侧的数值严格单调递减&#xff0c;最小值右侧的数值严格单调递增。 三分的本质 三分和二分一样都是通过不断缩…...

LangFlow技术架构分析

&#x1f527; LangFlow 的可视化技术栈 前端节点编辑器 底层框架&#xff1a;基于 &#xff08;一个现代化的 React 节点绘图库&#xff09; 功能&#xff1a; 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...