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

线程池详解

线程池

什么是线程池:线程池就是管理一系列线程的资源池。

当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是等待下一个任务。

为什么要用线程池 / 线程池的好处:
  • **降低资源消耗。**通过重复利用已创建的线程降低线程创建和销毁造成的消耗

  • **提高响应速度。**当任务到达时,任务可以不需要等到线程创建久能立即执行。

  • **提高线程的可管理性。**统一管理线程,避免系统创建大量同类线程而导致消耗完内存。

线程池的应用:

比如我们系统 Web 服务器用的 Tomcat,Tomcat 处理每一个请求都会从线程连接池里面用一个线程去处理,又比如说我们连接数据库会用对应连接池 Druid德鲁伊等

还有实际开发中的定时任务,该任务的链路执行时间和过程都非常长,就会用一个线程池将该定时任务的请求进行处理。这样做好处就是可以及时返回结果给调用方,能够提高系统的吞吐量

// 请求直接交给线程池来处理 
public void push(PushParam pushParam) {try {pushServiceThreadExecutor.submit(() -> {handler(pushParam);});} catch (Exception e) {logger.error("pushServiceThreadExecutor error, exception{}:", e);}
}
如何创建线程池
  • 通过 ThreadPoolExecutor 构造函数来创建(推荐)
  • 通过 Executor 框架的工具类 Executors 来创建

为什么不推荐使用内置线程池(通过 Executor 框架的工具类 Executors 来创建):通过 ThreadPoolExecutor 构造函数的方式,这样的处理方式让开发人员更加明确线程池的运行规则,规避资源耗尽的风险

线程池常见参数有哪些
ThreadPoolExecutor 3个最重要的参数
  • corePoolSize:任何队列未达到队列容量时,最大可以同时运行的线程数量

  • maxinumPoolSize:当任务队列存放的任务达到队列容量的时候的最大线程数量。任何队列中存放的任务达到队列容量的时候,会创建新的线程运行任务。否则根据拒绝策略处理新任务。

  • workQueue:任务队列:新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中

    keepAliveTime:线程池中的线程数量大于 corePoolSize 的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了 keepAliveTime才会被回收销毁。非核心线程空闲后,保持存活的时间;设置为0,表示多余的空闲线程会被立即终止

    unit:keepAliveTime 参数的时间单位

    threadFactory:

    handler:饱和策略

线程池的饱和策略:
  • ThreadPoolExecutor.AbortPolity:抛出 RejectedExecutionException来拒绝新任务的处理
  • ThreadPoolExecutor.CallerRunsPolicy:
  • ThreadPoolExecutor.DiscardPolicy:
  • .DiscardPolicy:
  • .DiscardOldestPolicy:
如何处理这些参数 / 线程池大小怎么设置
CPU密集型任务(N + 1)

消耗CPU资源,将线程数设置为 N(CPU 核心数)+1,比 CPU 核心数多出来一个线程是为了防止线程阻塞、锁等原因导致的任务暂停带来的影响。一旦某个线程被阻塞,释放了 cpu 资源,多一个线程就可以充分利用 CPU 的空闲时间

IO密集型

系统会用大部分时间处理 I/O 操作,而线程等待 I/O 操作会被阻塞,释放CPU资源,这是就可以将 CPU 交出给其它线程使用。因此在 I/O 密集型任务应用中,可以多配置一些线程:

最佳线程数 = CPU核心数 * (1/CPU利用率) = CPU 核心数 * (1 + (I/O耗时 / CPU耗时))

线程池过小:当有大量请求需要处理,系统响应比较慢影响体验,甚至会出现在任务队列大量堆积导致OOM

线程池过大:大量线程同时争取 CPU 资源,这样会导致大量的上下文切换,增加线程执行时间,影响整体执行效率

CPU密集型、IO密集型

如果你的应用主要是利用cpu计算,也就是更多的是消耗cpu资源的话,那么就是cpu密集型,如果更多的是和IO相关的,比如接收一个前端请求--解析参数--查询数据库--返回给前端这样的,那么就是IO密集型的。

线程池处理任务的流程

图解线程池实现原理

  1. 如果当前运行的线程数小于核心线程数,那么就会新建一个线程来执行任务
  2. 如果当前运行的线程数等于或大于核心线程数,但是小于最大线程,那么就把该任务放入到任务队列里等待执行
  3. 如果向任务队列投放任务失败,但是当前运行的线程数是小于最大线程数的,就新建一个线程来执行任务
  4. 如果当前运行的线程数已经等同于最大线程数了,新建线程将会是当前运行的线程超出最大线程数,那么当前任务会被拒绝,饱和(拒绝)策略会调用RejectedExecutionHandler.rejectedExecution() 方法

Excetors 工具类提供了哪些线程池?有什么问题吗?

Excetors 工具类提供的常见线程池如下:

  • FixedThreadPool:固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
  • SingleThreadExecutor:只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
  • CachedThreadPool:可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
  • ScheduledThreadPool:给定的延迟后运行任务或者定期执行任务的线程池。

Excetors 工具类提供的常见线程池的弊端如下:

  • FixedThreadPoolSingleThreadExecutor:使用的是无界的 LinkedBlockingQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
  • CachedThreadPool:使用的是同步队列 SynchronousQueue, 允许创建的线程数量为 Integer.MAX_VALUE ,如果任务数量过多且执行速度较慢,可能会创建大量的线程,从而导致 OOM。
  • ScheduledThreadPoolSingleThreadScheduledExecutor:使用的无界的延迟阻塞队列DelayedWorkQueue,任务队列最大长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致 OOM。
http://www.lryc.cn/news/336066.html

相关文章:

  • mybatis(5)参数处理+语句查询
  • 数据应用OneID:ID-Mapping Spark GraphX实现
  • 第6章 6.2.3 : readlines和writelines函数 (MATLAB入门课程)
  • Matlab应用层生成简述
  • 每日一题(leetcode1702):修改后的最大二进制字符串--思维
  • PHP自助建站系统,小白也能自己搭建网站
  • 计算机视觉 | 基于 ORB 特征检测器和描述符的全景图像拼接算法
  • Scala - 函数柯里化(Currying)
  • Switch-case
  • 蓝桥杯-单片机基础16——利用定时计数中断进行动态数码管的多窗口显示
  • 2024/4/5—力扣—下一个排列
  • xss.pwnfunction-Ugandan Knuckles
  • LabVIEW和2D激光扫描的受电弓滑板磨耗精确测量
  • Linux第87步_阻塞IO实验
  • C/C++基础----常量和基本数据类型
  • 什么是生成式AI?有哪些特征类型
  • 《Linux C/C++服务器开发实践》之第7章 服务器模型设计
  • SSH穿透ECS访问内网RDS数据库
  • python 有哪些函数
  • ubuntu web端远程桌面控制
  • PCL 点到三角形的距离(3D)
  • C# wpf 嵌入外部程序
  • 【ELK】ELK企业级日志分析系统
  • 详细的讲一下java的接口回调
  • 如何将powerpoint(PPT)幻灯片嵌入网页中在线预览、编辑并保存到服务器?
  • [Java基础揉碎]日期类
  • 4.10作业
  • Hive概述与基本操作
  • 安装 FFmpeg
  • 18、差分