最新要闻
- 今日聚焦!导演陆川:AI 15秒生成的海报 比专业公司一个月做得还好
- 318川藏线突现雪崩 行车记录仪拍下惊险一幕
- 世界微资讯!5500元 一图读懂特斯拉补能神器CyberVault赛博充:专为中国用户定制
- 信息:“喝酒吃药”卷土重来!公募基金重仓股TOP50中贵州茅台仍在榜首,还有17只票被增持(附表)
- 【报资讯】南漳:油菜花开春意浓
- 世界观察:揭秘你不知道的“寒食节”:春秋时期延续至今 要吃青团、凉面
- 车评人批丰田埃尔法不装后防撞梁:一个倒车碰撞车体就变形
- 天天热资讯!消灭刘海挖孔!曝iPhone 17 Pro将是首款真全面屏苹果手机
- 全球信息:5A级薄荷抗菌 凉感冰丝太爽:卡帝乐鳄鱼夏季平角裤7.3元/条发车
- 观焦点:标准版终于要上高刷了!iPhone或2025年全系列引入LTPO技术
- 环球微速讯:四月份去贵州旅游好吗_四月份去贵州旅游
- 戴苹果手表致手腕红肿你遇见没?苹果客服回应:或与皮肤敏感有关
- 全球微资讯!《拿破仑》年内公映
- 世界热消息:北汽越野BJ90狂降71万:打2.8折提换壳奔驰GLS
- 天天观天下!就是玩!马斯克将推特图标换成柴犬头像:还发了一幅漫画
- 每日讯息!村民回应网传奥迪轿车被当祭品焚烧:确实是意外
手机
iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- iphone11大小尺寸是多少?苹果iPhone11和iPhone13的区别是什么?
- 警方通报辅警执法直播中被撞飞:犯罪嫌疑人已投案
- 男子被关545天申国赔:获赔18万多 驳回精神抚慰金
- 3天内26名本土感染者,辽宁确诊人数已超安徽
- 广西柳州一男子因纠纷杀害三人后自首
- 洱海坠机4名机组人员被批准为烈士 数千干部群众悼念
家电
JAVA多线程并发编程-避坑指南
作者:京东零售肖朋伟
(资料图片仅供参考)
一、前言
开发过程中,多线程的应用场景可谓十分广泛,可以充分利用服务器资源,提高程序处理速度。我们通常也会使用池化技术,去避免频繁创建和销毁线程。
本篇旨在基于编码规范、工作中积累的研发经验等,整理在多线程开发的过程中需要注意的部分,比如不考虑线程池参数、线程安全、死锁等问题,将会存在潜在极大的风险。并且对其进行根因分析,避免每天踩一坑,坑坑不一样。
二、多线程并发场景有哪些坑?
1、“不正确的创建”线程池
常规来说,线程资源必须通过线程池提供,不允许在应用中自行显式创建线程,京东 JAVA 代码规范也明确表示 “线程资源必须通过线程池提供,不允许在应用中自行显式创建线程”,但是创建线程池的方式也有很多种,不能滥用。
常见创建线程池方式如:通过 JDK 提供的 ThreadPoolExecutor、ScheduledThreadPoolExecutor 以及 JDK 7 开始引入的 ForkJoinPool 创建,还有更方便的 Executors 类以及 spring 提供的 ThreadPoolTaskExecutor 等。其关系如下图:
Executors 的 newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor、newCachedThreadPool 方法在底层都是用的 ThreadPoolExecutor 实现的。虽然更加方便,但也增加了风险,如果不清楚其相关实现,在特定场景可能导致很严重的问题,所以开发规范中,会严格禁用此类用法。它们的弊端如下:
(1)FixedThreadPool 和 SingleThreadPool 允许的请求任务队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
(2)CachedThreadPool 和 ScheduledThreadPool 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
2、线程池“参数设置不合理”
上面提到了 任务队列 和 最大线程数的重要性,下面通过 ThreadPoolExecutor 介绍线程池其他核心参数,都应该根据场景合理配置,详细如下:
注意在设置任务队列时,需要考虑有界和无界队列,使用有界队列时,注意线程池满了后,被拒绝的任务如何处理。使用无界队列时,需要注意如果任务的提交速度大于线程池的处理速度,可能会导致内存溢出。
对于拒绝策略,首先要明确的一点是“拒绝策略的执行线程是提交任务的线程,而不是子线程”。JDK 的 ThreadPoolExecutor 提供了 4 种拒绝策略:
对于自定义拒绝策略,不同场景应选择相应的拒绝策略,抛开应用场景讲技术会显得苍白,大家可以参考常见技术框架的处理方式:
相关拓展:
相信大多数京东开发者都遇到过,JSF 依赖服务触发拒绝策略的现象,即抛出线程拒绝异常。这是当 Provider的 业务线程池满了,无可用线程池的时候,会返回一个异常给 Consumer,告知 Consumer 该 Provider 线程池已耗尽。如图:
当然这种异常场景,根本原因并非线程池配置不合理,应该关注服务提供方性能瓶颈,关于线程池的配置,其实没用一个统一或者可推荐的配置可以套用。对于 JSF 业务线程池,默认使用的是伸缩无队列线程池,其也提供了配置方式。
3、局部线程池“使用后不销毁回收”
线程会消耗宝贵的系统资源,比如内存等,所以是很不推荐使用局部线程池(未预先创建的线程池,用完就可以销毁,下次用时还会创建)的;但是如果某些特殊场景确实使用了局部线程池,那么应该在用完后,主动销毁。主动销毁线程池主要有两种方式:
为了更深入的理解两个问题:
(1)到底是否所有局部创建的线程池都需要主动销毁?
(2)为什么 Dubbo 中的线程拒绝策略 AbortPolicyWithReport 使用了 Executors.newSingleThreadExecutor(),并且没有主动销毁动作?
我们需要从 GC 角度进行分析。要知道对象什么时候死亡,我们需要先知道 JVM 的 GC 是如何判断对象是可以回收的。JAVA 是通过可达性算法来来判断对象是否存活的。这个算法的基本思路就是通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是不可用的。
对于线程池而言,在 ThreadPoolExecutor 类中具有非静态内部类 Worker,用于表示当前线程池中的线程,因为非静态内部类对象具有外部包装类对象的引用,所以当存在线程 Worker 对象时,线程池不会被 GC 回收。也就是说,线程池没有引用,且线程池内没有存活线程时,才是可以被 GC 回收的。应注意的是线程池的核心线程默认是一直存活的,除非核心线程数为 0 或者设置了 allowCoreThreadTimeOut 允许核心消除空闲时销毁。
我们对 Executors 创建的三种线程池进行比较:
三种类型的线程池与 GC 关系:
(1)CachedThreadPool:没有核心线程,且线程具有超时时间,可见在其引用消失后,等待任务运行结束且所有线程空闲回收后,GC开始回收此线程池对象;
(2)FixedThreadPool:核心线程数及最大线程数均为 nThreads,并且在默认 allowCoreThreadTimeOut 为 false 的情况下,其引用消失后,核心线程即使空闲也不会被回收,故 GC 不会回收该线程池;
(3)SingleThreadExecutor:在创建时实际返回的是 FinalizableDelegatedExecutorService 类的对象,该类重新了 finalize() 函数执行线程池的销毁,该对象持有 ThreadPoolExecutor 对象的引用,但 ThreadPoolExecutor 对象并不引用 FinalizableDelegatedExecutorService 对象,这使得在 FinalizableDelegatedExecutorService 对象的外部引用消失后,GC 将会对其进行回收,触发 finalize 函数,而该函数仅仅简单的调用 shutdown 函数关闭线程,在所有当前的任务执行完成后,回收线程池中线程,则 GC 可回收线程池对象
所以结论是:CachedThreadPool 及 SingleThreadExecutor 的对象在不显式销毁时,且其对象引用消失的情况下,可以被 GC 回收;FixedThreadPool 对象在不显式销毁,且其对象引用消失的情况下不会被 GC 回收,会出现内存泄露。因此无论使用什么线程池,使用完毕后均调用 shutdown 是一个较为安全的编程习惯。
4、线程池处理“刚启动时效率低”
默认情况下,即使是核心线程也只能在新任务到达时才创建和启动。对于 ThreadPoolExecutor 线程池可以使用 prestartCoreThread(启动一个核心线程)或 prestartAllCoreThreads(启动全部核心线程)方法来提前启动核心线程。
5、“不合理的使用共享线程池”
提交异步任务,不指定线程池,存在最主要问题是非核心业务占用线程资源,可能会导致核心业务收影响。因为公共线程池的最大线程数、队列大小、拒绝策略都比较保守,可能引发各种问题,常见场景如下:
(1)CompleteFuture 提交异步任务,不指定线程池。
CompleteFuture 的 supplyAsync 等以 *Async 为结尾的方法,会使用多线程异步执行。可以注意到的是,它也允许我们不携带多线程提交任务的执行线程池参数,这个时候是默认使用的 ForkJoinPool.commonPool()。
ForkJoinPool 最适合的是计算密集型的任务,如果存在 I/O,线程间同步,sleep() 等会造成线程长时间阻塞的情况时,最好配合使用 ManagedBlocker。ForkJoinPool 默认线程数取决于 parallelism 参数为:CPU 处理器核数-1,也允许通修改 Java 系统属性 "java.util.concurrent.ForkJoinPool.common.parallelism" 进行自定义配置。
(2)JDK 8 引入的集合框架的并行流 Parallel Stream。
Parallel Stream 也是使用的 ForkJoinPool.commonPool(),但有一点区别是:Parallel Stream 的主线程 (提交任务的线程)是会去参与处理的;比如 8 核心的机器执行 Parallel Stream 是有 8 个线程,而 CompleteFuture 提交的任务只有 7 个线程处理。不建议使用是一致的。
(3)@Async 提交异步任务,不指定线程池。
SpringBoot 2.1.9 之前版本使用 @Async 不指定 Executor 会使用 SimpleAsyncTaskExecutor,该线程池默认来一个任务创建一个线程,若系统中不断的创建线程,最终会导致系统占用内存过高,引发 OutOfMemoryErro r错误。SpringBoot 2.1.0 之后版本引入了 TaskExecutionAutoConfiguration,其使用 ThreadPoolTaskExecutor 作为 默认 Executor,通过 TaskExecutionProperties.Pool 可以看到其配置默认核心线程数:8,最大线程数:Integet.MAX_VALUE,队列容量是:Integet.MAX_VALUE,空闲线程保留时间:60s,线程池拒绝策略:AbortPolicy。
虽然可以通实现 AsyncConfigurer 接口等方式,自行配置线程池参数,但仍不建议使用公共线程池。
6、主线程“等待时间不合理”
(1)应尽量避免使用 CompletableFuture.join(),Future.get() 这类不带有超时时间的阻塞主线程操作。
(2)for 循环使用 future.get(long timeout, TimeUnit unit)。此方法允许我们去设置超时时间,但是如果主线程串行获取的话,下一个 future.get 方法的超时时间,是从第一个 get() 结束后开始计算的,所以会导致超时时间不合理。
7、提交任务“不考虑子线程超时时间”
(1)主线程 Future.get 虽然超时,但是子线程依然在执行?
比如当通过 ExecutorService 提交一个 Callable 任务的时候,会返回一个 Future 对象,Future 的 get(long timeout, TimeUnit unit) 方法时,如果出现超时,则会抛出 java.util.concurrent.TimeoutException;但是,此时 Future 实例所在的线程并没有中断执行,只是主线程不等待了,也就是当前线程的 status 依然是 NEW 值为 0 的状态,所以当大量超时,可能就会将线程池打满。
提到中断子线程,会想到 future.cancel(true) 。那么我们真的可以中断子线程吗?首先 Java 无法直接其他线程的,如果非要实现此功能,也只能通过 interrupt 设置标志位,子线程执行到中间环节去查看标志位,识别到中断后做后续处理。理解一个关键点,interrupt() 方法仅仅是改变一个标志位的值而已,和线程的状态并没有必然的联系。
(2)子线程的任务都应该有一个合理的超时时间。
比如子线程调用 JSF/ HTTP 接口等,一定要检查超时时间配置是否合理。
8、并发执行“线程不安全”操作
多线程操作同一对象应考虑线程安全性。常见场景比如 HashMap 应该换成 ConcurrentHashMap;StringBuilder 应该换成 StringBuffer 等。
9、“不考虑线程变量”的传递
提交到线程池执行的异步任务,切换了线程,子线程在执行时,获取不到主线程变量中存储的信息,常见场景如下:
(1)类似 BU 等,为了减少参数透传,可能存在了 ThreadLocal 里面;
(2)客户的登录状态,LoginContext 等信息,一般是线程变量;
如果解决此问题,可以参考 Transmittable-Thread-Local 中间件提供的解决方案等。
10、并发会“增大出现死锁的可能性”
多线程不只是程序中提交到线程池执行,比如打到同一容器的 http 请求本身就是多线程,任何多线程操作都有死锁风险。使用业务线程池的并发操作需要更加注意,因为更容易暴露出来“死锁”这个问题。
比如 Mysql 事务隔离级别为 RR 时,间隙锁可能导致死锁问题。间隙锁是 Innodb 在可重复读提交下为了解决幻读问题时引入的锁机制,在执行 update、delete、select ... for update 等语句时,存在以下加间隙锁情况:
(1)有索引,当更新的数据存在时,只会锁定当前记录;更新的不存在时,间隙锁会向左找第一个比当前索引值小的值,向右找第一个比当前索引值大的值(没有比当前索引值大的数据时,是 supremum pseudo-record,可以理解为锁到无穷大)。
(2)无索引,全表扫描,如果更新的数据不存在,则会根据主键索引对所有间隙加锁。
当并发执行数据库事务(事务内先更新,后新增操作),当更新的数据不存在时,会加间隙锁,然后执行新增数据需要其他事务释放在此区间的间隙锁,则可能导致死锁产生;如果是全表扫描,问题可能更严重。
11、“不考虑请求过载”
最后,我们设置了合理的参数,也注意优化了各种场景问题,终于可以大胆使用多线程了。也一定要考虑对下游带来的影响,比如数据库请求并发量增长,占用 JDBC 数据库连接、给依赖 RPC 服务带来性能压力等。
参考文章链接:
•http://www.kailing.pub/article/index/arcid/255.html
•https://blog.csdn.net/sinat_15946141/article/details/107951917
•https://segmentfault.com/a/1190000016461183?utm_source=tag-newest
•https://blog.csdn.net/v123411739/article/details/106609583/
•https://blog.csdn.net/whzhaochao/article/details/126032116
•https://www.kuangstudy.com/bbs/1407940516238090242
关键词:
-
今日热闻!安装MYSQL_5.0/8.0教程(附数据库和客户端工具下载链接)
1 Mysql5 7下载网盘下载(推荐):链接:https: pan quark cn s d98d2536f847提取码:kbyN官网下载:mysql下载地址:https: dev
来源: -
今日热议:易基因: oxRRBS+RRBS揭示炎症性肠病导致发育异常的表观遗传机制|甲基化研究
大家好,这里是专注表观组学十余年,领跑多组学科研服务的易基因。2020年12月31日,美国明尼苏达大学Nat...
来源: JAVA多线程并发编程-避坑指南
今日热闻!安装MYSQL_5.0/8.0教程(附数据库和客户端工具下载链接)
今日热议:易基因: oxRRBS+RRBS揭示炎症性肠病导致发育异常的表观遗传机制|甲基化研究
今日聚焦!导演陆川:AI 15秒生成的海报 比专业公司一个月做得还好
318川藏线突现雪崩 行车记录仪拍下惊险一幕
世界微资讯!5500元 一图读懂特斯拉补能神器CyberVault赛博充:专为中国用户定制
信息:“喝酒吃药”卷土重来!公募基金重仓股TOP50中贵州茅台仍在榜首,还有17只票被增持(附表)
【报资讯】南漳:油菜花开春意浓
世界观察:揭秘你不知道的“寒食节”:春秋时期延续至今 要吃青团、凉面
车评人批丰田埃尔法不装后防撞梁:一个倒车碰撞车体就变形
天天热资讯!消灭刘海挖孔!曝iPhone 17 Pro将是首款真全面屏苹果手机
全球信息:5A级薄荷抗菌 凉感冰丝太爽:卡帝乐鳄鱼夏季平角裤7.3元/条发车
观焦点:标准版终于要上高刷了!iPhone或2025年全系列引入LTPO技术
天天热讯:扫叶林风后,拾薪山雨前
day35 860. 柠檬水找零
当前快播:全网最详细中英文ChatGPT-GPT-4示例文档-智能AI辅助写作从0到1快速入门——官网推荐的48种最佳应用场景(附python/node.js/
环球微速讯:四月份去贵州旅游好吗_四月份去贵州旅游
戴苹果手表致手腕红肿你遇见没?苹果客服回应:或与皮肤敏感有关
全球微资讯!《拿破仑》年内公映
世界热消息:北汽越野BJ90狂降71万:打2.8折提换壳奔驰GLS
天天观天下!就是玩!马斯克将推特图标换成柴犬头像:还发了一幅漫画
每日讯息!村民回应网传奥迪轿车被当祭品焚烧:确实是意外
前沿热点:美克生能源携手新凤鸣集团 打造5MWh用户侧储能电站
天天关注:男生1900元买iPhone 14 Pro Max开机竟是安卓系统:商家被平台封禁
世界速看:铠侠成功研制出HLC闪存!SSD容量暴增:速度/耐用性比QLC还拉跨
环球观热点:2999元起 魅族20系列首销一秒破亿!京东、天猫多平台销冠
世界即时看!ChatGPT出现大规模封号; 我国移动网络IPv6流量首次突破50%;菜鸟首个航空运货中心落户深圳|Do早报
观焦点:Centos 分割卷组
焦点消息!中国恒大:公司与债权人特别小组成员签订三份重组支持协议
三利好助A股四月开门红 “小阳春”行情值得期待
每日消息!交易拥挤度创23年纪录 TMT板块还能热多久
要闻:国际金融市场早知道:4月4日
环球快资讯丨大家都越骂越买?iPhone 13成全球最畅销手机 小米为安卓挽尊
每日消息!国内将上映!真人电影《小美人鱼》最新预告 这美人鱼网友看完感叹
今日热闻!AMD Zen4加持!ROG首款掌机来了:比Switch强大太多
读SQL进阶教程笔记07_EXISTS谓词
gw文件用什么软件打开手机上_gw文件用什么软件打开
环球快播:《王者荣耀》杨玉环新皮肤官宣:时尚芭莎指导 春天的神女
环球速讯:外卖超20元才送?江苏省消保委:停止被迫“凑单”
前沿热点:LCD电视跌成白菜价 面板一哥京东方去年利润下滑71%
腰粗大树刮断砸中小轿车 女车主:不慌 先拍个视频
【当前独家】网传青海现野生大熊猫!专家:绝对不可能
【聚看点】c语言分解质因数_分解质因数的概念
全球要闻:网红书店言几又被强制执行6400万
centos 8 上sql server 安装
【全球热闻】HP发布暗影精灵9系列游戏本:RTX 4080干到13999元
【新要闻】1199元!海康Mage20 Pro家庭存储NAS发布:支持最大40TB容量
每日看点!对线!枪迷称落后22分曼联追分无望,内维尔怼阿森纳前15年战绩差
天天最资讯丨Flask
世界新消息丨Autoconfiguration详解——自动注入配置参数
当前速看:1080. 根到叶路径上的不足节点
【环球速看料】上下五千年 熊猫第一次有了站姐
环球滚动:菜鸟首个航空货运中心落户深圳:包裹效率提升30%
世界焦点!计算机系统的组成(1硬件系统篇)
昆自股份拟通过拍卖方式购买14套房产作为公司经营活动场所 评估价值为3516.56万
引入人工智能!米哈游刘伟:《崩坏:星穹铁道》将在NPC加入AI
终结者来临?让开发者都畏惧的AI真的对人类有威胁吗
【独家焦点】重回1元/包:中石化出品原生竹浆90抽*3层抽纸大促
观天下!11.98万起 广汽埃安爆款2023上新:48小时订单破万
我国民营火箭公司天兵科技将研发空天飞机:载100人全球任意地点往返
债市日报:4月3日
天天快报!上古卷轴五强制随从代码_上古卷轴5强制随从代码
天天消息!夜宿海底捞引争议!究竟谁在留宿?多数都为旅游的年轻人
当前资讯!00后男生坚持做副业月入万元:父母能力有限 只能靠自己
即时焦点:等等党赢了!全球车企正迈向产量过剩:价格战或卷土重来
15999元 微星泰坦GP68HX游戏本预售:16寸2.5K屏+RTX 4080
焦点消息!2023 LPL季后赛爆冷:BLG 3比0横扫WBG 提前进入四强
dmesg 时间误差现象
天天消息!韩国音乐下载网站(求专门下载韩国歌曲的网站··)
最新快讯!网信办:移动物联网连接数首次超移动电话用户数 已县县通5G
炫富风波后中金人均降薪20万 平均收入降至约78万元
3月朋友圈疯传的十大谣言:闰月上坟祸事临门、反复烧开水致癌都是假的
首次融入生成式AI!百度地图V18版发布:数字人小姐姐“坐”你副驾
环球快看:超威超能石墨烯电池旧车挑战赛:平均续航103.6km仍余电
环球快看点丨记录--Canvas实现打飞字游戏
微信小程序订阅消息开发指南(java)
环球动态:Midjourney? 文心一格? 一张思维导图带你了解图片生成AI
张同乐-从零开始,打造高效可靠的Locust性能测试
星城控股20亿元私募债券获上交所受理
张颂文败给了新海诚?同上映11天:两部新片口碑相仿 票房差距5亿
世界速递!成都一公寓按排量收停车费每月最低1200元 官方回应:可自行定价
北野武悼念坂本龙一:朋友们都不在了 只剩下我一个人
环球新动态:无任何定语!真我GT Neo5 SE预售销量破纪录:1999元真香
世界微头条丨《小美人鱼》新镜头截图:爱丽儿抚摸王子脸 王子尬笑
极氪001车主吐槽:语音助手突然出现故障,无法语音识别指令
Flask框架cbv的写法、请求与响应、请求扩展、session源码分析、闪现
HEU KMS Activator 30.2.0全能系统数字许可激活工具 (全新激活版)
DecisionTreeClassifier&DecisionTreeClassRegression
GPT-4 还没玩透,GPT-5已遭众人围剿
Python常见面试题015.请实现一个如下功能的函数
焦点快报!任天堂或将进军手游!宣布与DeNA合作创立新公司
世界通讯!终于不用忍受熊孩子了!复兴号智能动车上线“静音车厢”
世界微资讯!真不是电影特效!武汉大暴雨白天秒变黑夜:雷电大风齐上阵
当前动态:3月新势力销量排名:理想断崖领先 第二名只卖一款车
身残技坚 国外一《守望先锋2》眼睛残疾玩家达到大师段位
【焦点热闻】《秋叶原物语2导演剪辑版》Switch中文版开启预购 4月20日发售
全球即时:统一观测丨使用 Prometheus 监控 Nginx Ingress 网关最佳实践
世界观热点:第06章 索引的数据结构
阿里云EMAS移动测试最佳实践|马来西亚第一大电子钱包通过EMAS测试提效6倍
提供一个SpringCloud Gateway获取body参数的方法