线程池核心九问

2023-11-03 03:10
文章标签 线程 核心 九问

本文主要是介绍线程池核心九问,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.什么是线程池?

线程池就是提前创建若干个线程,
如果有任务需要处理,线程池里的线程就会处理任务,处理完之后线程并不会被销毁,而是等待下一个任务。减少频繁创建和销毁线程消耗系统资源。

2.为什么要用线程池 ?

频繁创建、销毁 线程,将是对系统资源的极大浪费。
因此,实际开发中我们将使用线程池来管理、复用线程。
使用线程池,可以
1.降低资源消耗: 重复利用线程,减少创建和销毁造成的消耗。
2.提升响应速度: 任务到达,不需要创建,立即执行。
3.提高可管理型: 线程是CPU调度和分派的基本单位,
如果无限制地创建,不仅会消耗系统资源,还会降低系统稳定性。
使用线程池可以统一进行 分配、调优和监控。

3.创建线程池的方式?

Java从1.5 Executors 类提供四种创建线程池方式
1).newSingleThreadExecutor()

  • 单个线程的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务,(队列长度-Integer.MAX_VALUE)

2).newFixedThreadPool(maximumPoolSize)

  • **定最大线程数,线程池 **,核心线程数=最大线程数=设置的线程数,可控制线程最大并发数,(队列长度-Integer.MAX_VALUE);

3).newCachedThreadPool()

  • 可缓存线程池,有任务才新建线程,闲置线程保存60秒 ,(最大线程数长度-Integer.MAX_VALUE)

4).newScheduledThreadPool(corePoolSize)

  • 定核心线程数,线程池,支持定时及周期性任务执行。(最大线程数长度-Integer.MAX_VALUE)
    在这里插入图片描述

在这里插入图片描述

4.为什么不建议使用 Executors静态工厂构建线程池

主要原因:队列堆积,有OOM风险
1.FixedThreadPool 和 SingleThreadPool
允许的请求队列(底层实现是LinkedBlockingQueue)长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM

2.CachedThreadPool 和 ScheduledThreadPool
允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

5.线程池参数

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler) { }

corePoolSize:核心线程数量,一直存在,除非 allowCoreThreadTimeOut设置为true
maximumPoolSize:线程池允许的最大线程池数量 10
keepAliveTime:线程数量超过corePoolSize,空闲线程的最大超时时间
unit:超时时间的单位
workQueue:工作队列,保存未执行的Runnable 任务 (BlockingQueue 的实现类)
threadFactory:创建线程的工厂类
handler:当线程已满,工作队列也满了的时候,会被调用。被用来实现各种拒绝策略。

工作队列介绍
在这里插入图片描述

6.线程池的执行顺序

在这里插入图片描述

当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运
行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它
最终会收缩到 corePoolSize 的大小。

7.四种策略**

  • ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
  • ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常。
  • ThreadPoolExecutor.DiscardOldestPolicy:**丢弃队列最老的任务,然后重新提交被拒绝的任务 **
  • ThreadPoolExecutor.CallerRunsPolicy:由调用线程(提交任务的线程)处理该任务

8.实现原理

线程池由两个核心数据结构组成:
1)线程集合(workers):存放执行任务的线程,是一个HashSet;
2)任务等待队列(workQueue):存放等待线程池调度执行的任务,是一个阻塞式队列BlockingQueue;

8.如何合理设置线程池大小

任务一般可分为:CPU密集型、IO密集型、混合型,对于不同类型的任务需要分配不同大小的线程池。

  • CPU密集型任务: 尽量使用较小的线程池,一般为CPU核心数+1 ; (+1是利用等待空闲)
    • 因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,只能增加上下文切换的次数,因此会带来额外的开销。
  • IO密集型任务: 一般为2*CPU核心数。
    • IO密集型任务CPU使用率并不高,因此可以让CPU在等待IO的时候去处理别的任务,充分利用CPU时间。
  • 混合型任务
    • 可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理。

线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。
最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目

//java 中获取核心数 int availableProcessors = Runtime.getRuntime().availableProcessors();

9.线程池的动态规划参数动态化

JDK原生线程池ThreadPoolExecutor提供了如下几个public的setter方法,如下图所示:
在这里插入图片描述

JDK允许线程池使用方通过ThreadPoolExecutor的实例来动态设置线程池的核心策略,
以setCorePoolSize为方法例,
在运行期线程池使用方调用此方法设置corePoolSize之后,
线程池会直接覆盖原来的corePoolSize值,并且基于当前值和原始值的比较结果采取不同的处理策略。
对于当前值小于当前工作线程数的情况,说明有多余的worker线程,
此时会向当前空闲的worker线程发起中断请求以实现回收,
多余的worker在下次空闲的时候也会被回收;
对于当前值大于原始值且当前队列中有待执行任务,
则线程池会创建新的worker线程来执行队列任务,setCorePoolSize具体流程如下:
在这里插入图片描述

这篇关于线程池核心九问的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/335174

相关文章

SpringQuartz定时任务核心组件JobDetail与Trigger配置

《SpringQuartz定时任务核心组件JobDetail与Trigger配置》Spring框架与Quartz调度器的集成提供了强大而灵活的定时任务解决方案,本文主要介绍了SpringQuartz定... 目录引言一、Spring Quartz基础架构1.1 核心组件概述1.2 Spring集成优势二、J

Spring Boot3虚拟线程的使用步骤详解

《SpringBoot3虚拟线程的使用步骤详解》虚拟线程是Java19中引入的一个新特性,旨在通过简化线程管理来提升应用程序的并发性能,:本文主要介绍SpringBoot3虚拟线程的使用步骤,... 目录问题根源分析解决方案验证验证实验实验1:未启用keep-alive实验2:启用keep-alive扩展建

Java终止正在运行的线程的三种方法

《Java终止正在运行的线程的三种方法》停止一个线程意味着在任务处理完任务之前停掉正在做的操作,也就是放弃当前的操作,停止一个线程可以用Thread.stop()方法,但最好不要用它,本文给大家介绍了... 目录前言1. 停止不了的线程2. 判断线程是否停止状态3. 能停止的线程–异常法4. 在沉睡中停止5

Linux find 命令完全指南及核心用法

《Linuxfind命令完全指南及核心用法》find是Linux系统最强大的文件搜索工具,支持嵌套遍历、条件筛选、执行动作,下面给大家介绍Linuxfind命令完全指南,感兴趣的朋友一起看看吧... 目录一、基础搜索模式1. 按文件名搜索(精确/模糊匹配)2. 排除指定目录/文件二、根据文件类型筛选三、时间

Java捕获ThreadPoolExecutor内部线程异常的四种方法

《Java捕获ThreadPoolExecutor内部线程异常的四种方法》这篇文章主要为大家详细介绍了Java捕获ThreadPoolExecutor内部线程异常的四种方法,文中的示例代码讲解详细,感... 目录方案 1方案 2方案 3方案 4结论方案 1使用 execute + try-catch 记录

Spring Boot 中正确地在异步线程中使用 HttpServletRequest的方法

《SpringBoot中正确地在异步线程中使用HttpServletRequest的方法》文章讨论了在SpringBoot中如何在异步线程中正确使用HttpServletRequest的问题,... 目录前言一、问题的来源:为什么异步线程中无法访问 HttpServletRequest?1. 请求上下文与线

在 Spring Boot 中使用异步线程时的 HttpServletRequest 复用问题记录

《在SpringBoot中使用异步线程时的HttpServletRequest复用问题记录》文章讨论了在SpringBoot中使用异步线程时,由于HttpServletRequest复用导致... 目录一、问题描述:异步线程操作导致请求复用时 Cookie 解析失败1. 场景背景2. 问题根源二、问题详细分

Java多线程父线程向子线程传值问题及解决

《Java多线程父线程向子线程传值问题及解决》文章总结了5种解决父子之间数据传递困扰的解决方案,包括ThreadLocal+TaskDecorator、UserUtils、CustomTaskDeco... 目录1 背景2 ThreadLocal+TaskDecorator3 RequestContextH

java父子线程之间实现共享传递数据

《java父子线程之间实现共享传递数据》本文介绍了Java中父子线程间共享传递数据的几种方法,包括ThreadLocal变量、并发集合和内存队列或消息队列,并提醒注意并发安全问题... 目录通过 ThreadLocal 变量共享数据通过并发集合共享数据通过内存队列或消息队列共享数据注意并发安全问题总结在 J

异步线程traceId如何实现传递

《异步线程traceId如何实现传递》文章介绍了如何在异步请求中传递traceId,通过重写ThreadPoolTaskExecutor的方法和实现TaskDecorator接口来增强线程池,确保异步... 目录前言重写ThreadPoolTaskExecutor中方法线程池增强总结前言在日常问题排查中,