线程池的合理使用

线程池的合理使用

  • 一、简介
  • 二、为什么要使用线程池
  • 三、核心参数
  • 四、如何合理配置线程参数
    • 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.mzph.cn/pingmian/43317.shtml

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

相关文章

海外媒体发稿-全媒体百科

全球知名媒体机构 在全球范围内,有许多知名的新闻机构负责报道世界各地的新闻事件。以下是一些国外常见的媒体机构: AP(美联社)合众国际社(UPI)AFP(法新社)EFE(埃菲通讯社)Europa …

Nginx理论篇与相关网络协议

Nginx是什么? Nginx是一款由C语言编写的高性能、轻量级的web服务器,一个线程能处理多个请求,支持万级并发。 优势:I/O多路复用。 I/O是什么? I指的是输入(Input),O是指输出(Outp…

【安全设备】日志审计

一、什么是日志审计 日志审计是一站式的日志数据管理平台,主要致力于提供事前预警、事后审计的安全能力, 通过对日志数据的全面采集、解析和深度的关联分析,及时发现各种安全威胁和异常行为事件。日志审计是指通过集中采集信息系统中的各类信…

解决:Android Studio 突然打不开!提示Failed to create JVM:error code -1

Android studio1.5 一直用得好好的,突然有一天打不开,并提示: 可是系统配置中,java的配置也是正常的。 解决方法: 修改安装目录下的studio64.exe.vmoptions 文件 直接将文件内容改成: -Xms128m -Xmx512m…

谷歌+火狐浏览器——实现生成二维码并实现拖动——js技能提升

最新遇到的问题:前两个二维码拖动不了,只有第三个一维码生成后,才可以拖拽 【问题】:出现在都是绝对定位,但是没有指定z-index导致的。 解决办法:在方法中添加一个变量 renderDrag(id) {var isDragging f…

Python CuPy库:GPU加速的科学计算

更多Python学习内容:ipengtao.com 在数据科学和机器学习领域,处理大规模数据集常常需要巨大的计算资源。Python的CuPy库通过提供一个类似NumPy但运行在NVIDIA GPU上的接口,大幅提升了数组操作的速度,使得复杂的数值计算变得更加高…

基于FPGA的图像边缘检测(OV5640)

一、简介 1.应用范围 边缘主要存在于图像中目标与目标之间,目标与背景之间,区域与区域之间。 边缘检测的目的就是找到图像中亮度变化剧烈的像素点构成的集合,表现出来往往是轮廓。如果图像中边缘能够精确的测量和定位,那么&…

GaussDB关键技术原理:高性能(四)

GaussDB关键技术原理:高性能(三)从查询重写RBO、物理优化CBO、分布式优化器、布式执行框架、轻量全局事务管理GTM-lite等五方面对高性能关键技术进行了解读,本篇将从USTORE存储引擎、计划缓存计划技术、数据分区与分区剪枝、列式存…

Redis 7.x 系列【19】管道

有道无术,术尚可求,有术无道,止于术。 本系列Redis 版本 7.2.5 源码地址:https://gitee.com/pearl-organization/study-redis-demo 文章目录 1. 往返时间2. 管道技术3. 代码演示4. 其他批处理4.1 原生批处理命令4.2 事务4.3 脚本…

240708_昇思学习打卡-Day20-MindNLP ChatGLM-6B StreamChat

240708_昇思学习打卡-Day20-MindNLP ChatGLM-6B StreamChat 基于MindNLP和ChatGLM-6B实现一个聊天应用,本文进行简单记录。 环境配置 %%capture captured_output # 实验环境已经预装了mindspore2.2.14,如需更换mindspore版本,可更改下面mi…

Java | Leetcode Java题解之第224题基本计算器

题目&#xff1a; 题解&#xff1a; class Solution {public int calculate(String s) {Deque<Integer> ops new LinkedList<Integer>();ops.push(1);int sign 1;int ret 0;int n s.length();int i 0;while (i < n) {if (s.charAt(i) ) {i;} else if (s…

实施OPC UA网关以加速设备与MES系统之间的连接

在现代工业自动化中&#xff0c;信息化和智能化已成为企业提升竞争力的关键因素&#xff0c;为了实现生产过程的自动化和管理的高效化&#xff0c;工业自动化系统&#xff08;如OPC UA&#xff09;与制造执行系统&#xff08;MES&#xff09;的集成变得尤为重要。OPC UA&#x…

Pycharm 出现sdk is not defined for run configuration解决办法

第一步&#xff1a;运行->编辑配置 第二部&#xff1a;重新选择一下脚本路径和Python解释器 第三步&#xff1a;保存。重新运行

WebKit简介及其神秘的工作流程

在信息时代的巨浪中&#xff0c;互联网已经深深地渗透到了我们生活的每一个角落。作为连接我们与这个庞大网络世界的桥梁&#xff0c;网页浏览器无疑成为了我们生活中不可或缺的一部分。而在这些浏览器的背后&#xff0c;往往隐藏着一些强大而神秘的引擎&#xff0c;它们为浏览…

鸿蒙系统:未来智能生态的引领者

在当今这个日新月异的互联网领域&#xff0c;操作系统作为连接硬件与软件的桥梁&#xff0c;其重要性不言而喻。随着华为鸿蒙系统&#xff08;HarmonyOS&#xff09;的崛起&#xff0c;一场关于操作系统未来的讨论再次被推向高潮。 鸿蒙OS&#xff0c;华为的全新力作&#xff…

K8S篇之Ingress详解以及用法说明

一、Ingress简介 Ingress 是 Kubernetes 中用于管理和配置从集群外部访问集群内部服务的资源对象。它通过定义路由规则来控制外部流量的访问方式&#xff0c;支持基于 HTTP 和 HTTPS 的高级路由功能和安全性配置。 Ingress是一种HTTP方式的路由转发机制&#xff0c;为K8S服务配…

AGAST (角点检测)

AGAST检测原理 AGAST(Adaptive and Generic Accelerated Segment Test)算法是Elmar于2010年提出的特征检测算法,改进了FAST(Features from Accelerated Segment Test)特征检测方法,使其具有更快的速度和更好的鲁棒性。AGAST算法提供了比FAST算法更详细的特征标记方式和判断依…

【Python_GUI】tkinter模块、创建空白窗口

tkinter是使用Python进行窗口视觉设计的模块&#xff0c;它是Python的标准Tk GUI工具包的接口&#xff0c;在安装Python时&#xff0c;就自动安装了该模块。 使用tkinter模块开发时&#xff0c;最核心的就是各种组件的使用。生活中玩积木时&#xff0c;通过将不同形状的木板进…

A股本周在3000点以下继续筑底,本周依然继续探底?

夜已深&#xff0c;市场传来了3个浓烈的消息&#xff0c;炸锅了&#xff0c;恐有大事发生&#xff0c;马上告诉所有人&#xff1a; 消息面&#xff1a; 1、中国经济周刊首席评论员钮文新称&#xff1a;不要等中小投资者都彻底希望&#xff0c;销户离场了&#xff0c;才发现该…

【APK】Unity出android包,报错 Gradle build failed.See the Console for details

参考大佬的博客&#xff1a;报错&#xff1a;Gradle build failed.See the Console for details.&#xff08;已解决&#xff09;_starting a gradle daemon, 1 incompatible daemon co-CSDN博客 本地出Android包&#xff0c;Build失败 解决办法&#xff1a; 1.下载一个低版本…