线程池的合理使用

news2024/11/17 17:41:06

线程池的合理使用

  • 一、简介
  • 二、为什么要使用线程池
  • 三、核心参数
  • 四、如何合理配置线程参数
    • 1.1 corePoolSize && maximumPoolSize
    • 1.2 Handler 拒绝策略
      • 1.2.1AbortPolicy:
        • 优势:
        • 劣势:
      • 1.2.2 DiscardPolicy:
        • 优势:
        • 劣势:
      • 1.2.3 CallerRunsPolicy适合以下场景下使用:
        • 保证任务的执行:
        • 控制任务提交速率:
        • 避免任务丢失:
        • 优势:
        • 小结
    • 1.3 WorkQueue
  • 五、底层原理
    • 扩展
  • 六、注意事项
    • 1.1 项目中首页灵感盒子接口总结
    • 1.2 父子任务共用同一线程池,系统饥饿死锁
    • 1.3 future的使用注意事项

一、简介

线程池(Thread Pool)是一种基于池化思想管理线程的工具,在Java中的体现是ThreadPoolExecutor类。

二、为什么要使用线程池

Java 中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理地使用线程池能够带来以下好处。

  • 降低资源消耗。
    通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  • 提高线程的可管理性。
    在系统启动时就将对应业务线程池创建好,当任务到达时,任务可以不需要等到线程创建就能立即执行。
  • 提高响应速度。
    线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用线程池,必须对其实现原理了如指掌。

三、核心参数

  1. corePoolSize:核心线程数目,线程池中的常驻核心线程数
  2. maximumPoolSize:最大线程数目,线程池中能够容纳同时执行的最大线程数
  3. keepAliveTime:空闲线程的存活时间,当前线程池数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余空闲线程会被销毁直到只剩下corePoolSize个线程为止
  4. unit时间单位
  5. workQueue:阻塞队列,被提交但是尚未被执行的任务
  6. threadFactory:线程工厂-可以为线程创建时起个好名字
  7. handler:拒绝策略,表示当队列满了并且工作线程-大于等于线程池的数量最大线程数(maxinumPoolSize)时如何来拒绝请求执行的策略

四、如何合理配置线程参数

1.1 corePoolSize && maximumPoolSize

针对核心线程数 首先要分析业务的平时流量情况。通过监控和统计数据,了解每个时间段的请求量和并发数,核心线程数的配置也要考虑服务器的硬件资源。比如,CPU的核心数、内存容量等。确保配置的核心线程数不超过服务器硬件能够支持的最大并发数。

除了平时流量外,还需要考虑业务的长期趋势,此时maximumPoolSize就可以派上用场了,可以预留一部分线程来应对系统的流量突然激增,等到流量高峰过了,达到 keepAliveTime ,非核心线程就会进行回收,避免浪费了系统资源。

网上大部分情况一般都是看任务是IO密集型还是CPU密集型,如果是任务密集型配 2N,如果是CPU密集型则配置成N+1 (N代表CPU核心数),实际上大部分业务是比较复杂的,既有CPU密集型也有IO密集型,最好的方式是通过压测来确定下来,压测请参考:接口压测指南

1.2 Handler 拒绝策略

RejectedExecutionHandler的实现JDK自带的默认有4种

  • AbortPolicy:丢弃任务,抛出运行时异常
  • CallerRunsPolicy:由提交任务的线程来执行任务(主线程)
  • DiscardPolicy:丢弃这个任务,但是不抛异常
  • DiscardOldestPolicy:从队列中剔除最先进入队列的任务,然后再次提交任务
    线程池创建的时候,如果不指定拒绝策略默认就是 AbortPolicy 策略。当然,你也可以自己实现RejectedExecutionHandler 接口,比如将任务存在数据库或者缓存中,这样就能够从数据库或者缓存中获取到被拒绝掉的任务了。
    AbortPolicy和DiscardPolicy是ThreadPoolExecutor类中的两种常见拒绝策略,它们各自有不同的优缺点:

1.2.1AbortPolicy:

优势:

当线程池无法接受新任务时,立即抛出RejectedExecutionException异常,能够迅速通知调用者任务无法执行,保证系统稳定性。

劣势:

可能会导致任务丢失,对于一些重要的任务无法执行可能会对系统造成影响。

1.2.2 DiscardPolicy:

优势:

默默地丢弃无法处理的任务,不会抛出异常,不影响线程池继续处理其他任务,避免了异常抛出的开销。

劣势:

丢弃任务可能会造成任务丢失,不提供任何反馈给调用者,无法知道任务是否被执行。

选择使用哪种策略取决于具体的业务需求和系统设计。如果希望及时发现线程池无法处理新任务并进行处理,可以选择AbortPolicy;如果对于一些任务丢失可以接受,并且不希望抛出异常影响线程池的运行,可以选择DiscardPolicy。

1.2.3 CallerRunsPolicy适合以下场景下使用:

保证任务的执行:

当线程池无法接受新任务时,希望任务能够得到执行而不是被丢弃。

控制任务提交速率:

当某些情况下需要限制任务提交的速率,而且对任务执行时长没有特别要求时,可以选择使用CallerRunsPolicy。

避免任务丢失:

希望尽可能保证所有任务都能够被执行,即使需要调用线程来执行任务。

优势:

能够保证任务的执行,即使线程池无法接受新任务也能够确保任务不被丢弃。
可以避免因为任务被拒绝导致的异常抛出,保证线程池的稳定性。

小结

总之,CallerRunsPolicy可以保证任务得到执行,适用于对任务执行顺序要求不苛刻、重要性较高的场景下使用。

1.3 WorkQueue

任务队列:看需求情况选择无界队列还是有界队列

  • 线程池的任务队列本来起缓冲作用,但是如果设置的不合理会导致线程池无法扩容至max,这样无法发挥多线程的能力,导致一些服务响应变慢。
  • 队列长度要看具体使用场景,取决服务端处理能力以及客户端能容忍的超时时间等
  • 建议采用tomcat的处理方式,core与max一致,先扩容到max再放队列,不过队列长度要根据使用场景设置一个上限值,如果响应时间要求较高的系统可以设置为0。

五、底层原理

线程池底层原理

扩展

jdk自带的线程池与tomcat线程池区别

  • 线程创建时机:
    • 普通线程池:先使用核心线程,然后任务进入队列,再创建额外线程。
    • Tomcat 线程池:在核心线程忙时,会直接创建新的线程直到 maxThreads。
  • 队列的使用:
    • 普通线程池:核心线程忙时,任务排队,队列满时才创建新的线程。
    • Tomcat 线程池:在所有线程都忙时,任务才进入队列。

tomcat线程池更注重快速响应,会在核心线程忙时立即创建新线程,直到达到 maxThreads,只有在所有线程都忙时才使用队列来排队请求。

六、注意事项

1.1 项目中首页灵感盒子接口总结

由于线上核心线程数和最大线程数配置太小10-20,平时访问人数可能没有多少,但是一旦运营搞活动就会访问到首页的灵感盒子功能,导致请求进行堆积阻塞,处理不过来,导致对应服务大面积超时。

  1. 经过分析压测,最终调整核心线程数和最大线程数100-300,同时拒绝策略由调用者线程改为直接丢弃策略,同时利用本地缓存使得请求快速响应,最终达到一个单机的性能瓶颈,使得线程池的合理使用达到一个最佳的状态
  2. 同时代码中存在不同业务,共用了一个线程池,也就是父子任务共用一个线程池,这也是不合理的,应该根据不同业务使用不同的线程池,进行资源隔离
  3. 剩下就是针对超时时间的配置,不要太长,否则容易将线程资源耗尽,影响正常的请求

1.2 父子任务共用同一线程池,系统饥饿死锁

比如A方法调用B方法,AB方法称为父子任务,当他们都被同一个线程池执行时,一定条件下会出现以下场景:

  1. 父任务获取到线程池线程执行,而子任务则被暂存到队列中
  2. 当父任务沾满了线程池里所有的线程,等待子任务返回结果后,结束父任务
  3. 此时子任务由于在队列中,一直不能等到线程来处理,导致不能从队列中释放
  4. 父子任务互相等待,从而早饥饿死锁

1.3 future的使用注意事项

  • 项目中对于CompletableFuture的使用最好不要使用join,使用join会导致线程会一直阻塞直到超时时间才会返回,应该换成getNow,立即返回,即使没有拿到结果可以返回一个默认值。
  • 值得注意的是虽然 getNow 不会阻塞线程,但是线程没有执行完毕,例如
    执行allOf().get(超时时间)超时,依然可以拿到结果(默认值),在这种情况下 并不会回收线程 终止任务,网上说可以使用 cancel 或completed 等方法来进行终止关闭掉没有执行完毕的线程,遗憾的是本地经过测试并没有生效,也可能是自己使用的问题。

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

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

相关文章

3Python的Pandas:数据选取

1.数据选取操作 1.1. 选取单列 df[Q1]df[Q2]1.2. 选取多列 df[[team,Q1]]df.loc[:,[team,Q1]]1.3.选择行 使用指定索引选择 df[df.indexAck]选择前n行 df[0:3]df.iloc[:10,:]1.4. 前n行,每隔m选择一个 df[0:10:3]1.5. 条件选择 df[df.Q1>90]df[(df.teamC…

linxu驱动入门基础课一(GPIO控制LED灯)基于RK3568

虽然GPIO控制LED 是最简单的linux驱动,但是是初学者入门必须跨过的门槛,里面很多基础知识点,有GPIO的控制原理,字符设备驱动,设备树,gpio和pinctrl子系统,内核模块原理等等,这些知识…

APP明暗主题设置

1.preference.xml 增加一个暗色主题 SwitchPreference 2.每个 Activity 的 setContentView 前面增加 setTheme SharedPreferences sharedPreferences PreferenceManager.getDefaultSharedPreferences(this); if (sharedPreferences.getBoolean("switch_dark_theme"…

Windows下编译OpenSSL静态库

目录 1. 版本与下载地址 2. 下载与安装VS2015 3. 下载与安装Perl 4. 测试ActivePerl是否安装正确 5. 下载OpenSSL 6. 编译32位OpenSSL静态库 6.1 解压openssl-1.0.2l.tar.gz 6.2 打开VS2015 x86本机工具命令提示符 6.3 输入命令进入到openssl的目录中 6.4 执行配置命…

加密与安全_密钥体系的三个核心目标之不可否认性解决方案

文章目录 Pre概述不可否认性数字签名(Digital Signature)证书是什么证书使用流程 PKICA证书层级多级证书证书链是如何完成认证的? 其他疑问1. Alice能直接获取Bob的公钥,是否还需要证书?2. 为什么即使能直接获取公钥也…

世界人工智能大会 | 江行智能大模型解决方案入选“AI赋能新型工业化创新应用优秀案例”

日前,2024世界人工智能大会暨人工智能全球治理高级别会议在上海启幕。本次大会主题为“以共商促共享,以善治促善智”,汇聚了上千位全球科技、产业界领军人物,共同探讨大模型、数据、新型工业化等人工智能深度发展时代下的热点话题…

CLIP编码器调用时刚开始正常,然后输出全部变为NaN

碰到了这个问题:输入是正常的,输出全是NaN 网上办法不多,找了半天终于看到问题所在,但是没有说在哪里改的,故记录一下。 改一下模型精度就正常了,默认的是fp16,改为fp32即可 具体步骤如下&…

渲染引擎之ECS架构介绍

1.什么是ECS? 我们先简单地介绍一下什么是ECS: E -- Entity 实体,本质上是存放组件的容器C -- Component 组件,引擎所需的所有数据结构S -- System 系统,根据组件数据处理逻辑状态的管理器 ECS全称Entity-Component-…

免费压缩pdf文件大小软件收费吗?pdf如何压缩文件大小?12款压缩应用推荐!

在数字化时代,PDF文件因其跨平台、格式统一的特点而广受欢迎。然而,随着文件内容的增加,PDF文件的大小也逐渐增大,给存储和传输带来了诸多不便。因此,寻找一款合适的PDF压缩软件成为了许多用户的需求。本文将详细介绍1…

阿里开源语音理解和语音生成大模型FunAudioLLM

近年来,人工智能(AI)的进步极大地改变了人类与机器的互动方式,例如GPT-4o和Gemin-1.5等。这种转变在语音处理领域尤为明显,其中高精度的语音识别、情绪识别和语音生成等能力为更直观、更类人的交互铺平了道路。阿里开源…

# 昇思25天学习打卡营第10天 | 使用静态图加速

昇思25天学习打卡营第10天 | 使用静态图加速 文章目录 昇思25天学习打卡营第10天 | 使用静态图加速动态图的开启方式静态图的开启方式基于全局context的开启方式基于修饰器的开启方式 总结打卡 AI编译框架分为两种运行模式: 动态图模式: 计算图的构建和计…

C语言 | Leetcode C语言题解之第224题基本计算器

题目&#xff1a; 题解&#xff1a; int calculate(char* s) {int n strlen(s);int ops[n], top 0;int sign 1;ops[top] sign;int ret 0;int i 0;while (i < n) {if (s[i] ) {i;} else if (s[i] ) {sign ops[top - 1];i;} else if (s[i] -) {sign -ops[top - 1…

android文本长按复制

android文本长按复制 &#x1f4d6;1. 长按直接复制✅步骤一&#xff1a;定义一个TextView✅步骤二&#xff1a;为TextView注册长按事件✅步骤三&#xff1a;弹出系统复制功能 &#x1f4d6;2. 长按弹框确认复制✅步骤一&#xff1a;定义一个TextView✅步骤二&#xff1a;封装P…

3d模型设计要注意什么?---模大狮模型网

展览3D模型设计作为现代展示和艺术表达的重要手段&#xff0c;不仅仅是简单的视觉元素&#xff0c;更是整个展览体验的核心。在这个领域&#xff0c;设计师需要综合考虑美学、功能性以及技术实现的多重因素。本文将探讨在展览3D模型设计过程中需要注意的关键要点。 一、美学与视…

70.WEB渗透测试-信息收集- WAF、框架组件识别(10)

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 内容参考于&#xff1a; 易锦网校会员专享课 上一个内容&#xff1a;69.WEB渗透测试-信息收集- WAF、框架组件识别&#xff08;9&#xff09; 关于waf相应的识…

Java并发/多线程CompleteableFuture详解

目录 CompleteableFuture 创建 获得结果的方法 辅助方法 allOf和anyOf的区别 CompletableFuture 里大约有五十种方法&#xff0c;但是可以进行归类: 变换类 thenApply 消费类 thenAccept 执行操作类 thenRun thenApply/thenAccept/thenRun 结合转化类 thenCombine 结…

C++ 类和对象 构造 / 析构函数

一 类的6个默认成员函数&#xff1a; 如果一个类中什么成员都没有&#xff0c;简称为空类。 例&#xff1a; #include <iostream> class Empty {// 空类&#xff0c;什么成员都没有 }; 空类中真的什么都没有吗&#xff1f;并不是&#xff0c;任何类在什么都不写时&a…

使用预加载库优化 PostgreSQL 函数#postgresql认证

在 POSTGRESQL 中执行函数和过程 为了理解 PostgreSQL 的工作原理&#xff0c;我们首先要看一个简单的函数调用。下一个清单显示了一些简单的PostGIS代码&#xff1a; PgSQL test# timing Timing is on. test# SELECT * FROM hans.points WHERE id 1;id │ …

南大通用数据库-Gbase-8a-学习-44-DDLEVENT恢复

目录 一、环境信息 二、前景提要 1、情况描述 2、3号节点gc_recover日志截图 3、3号节点express日志截图 4、ddlevent截图 5、报错赋权语句分别在1节点和4节点执行 6、gcadmin 三、解决方法 1、描述 2、清理系统user表DDLEVENT 3、拷贝系统user表数据 &#xff08;…