深入了解Java线程池的原理和监控

 更新时间:2023年07月18日 08:31:15   作者:摆烂的小趴菜  
这篇文章主要介绍了深入了解Java线程池的原理和监控,创建Java线程需要给线程分配堆栈内存以及初始化内存,还需要进行系统调用,频繁地创建和销毁线程会大大降低系统的运行效率,这时候就要用到线程池,需要的朋友可以参考下

一、什么是线程池

简单看名字就知道是装有线程的池子,我们可以把要执行的多线程交给线程池来处理,和连接池的概念一样,通过维护一定数量的线程池来达到多个线程的复用。

二、线程池的好处

我们知道不用线程池的话,每个线程都要通过new Thread(xxRunnable).start()的方式来创建并运行一个线程,线程少的话这不会是问题

而真实环境可能会开启多个线程让系统和程序达到最佳效率,当线程数达到一定数量就会耗尽系统的CPU和内存资源,也会造成GC频繁收集和停顿

因为每次创建和销毁一个线程都是要消耗系统资源的,如果为每个任务都创建线程这无疑是一个很大的性能瓶颈。

所以,线程池中的线程复用极大节省了系统资源,当线程一段时间不再有任务处理时它也会自动销毁,而不会长驻内存。

三、使用线程池能解决的问题

1、效率

创建/销毁线程伴随着系统开销,过于频繁的创建/销毁线程,会很大程度上影响处理效率

例如:记创建线程消耗时间T1,执行任务消耗时间T2,销毁线程消耗时间T3,如果T1+T3>T2,那么是不是说开启一个线程来执行这个任务太不划算了!

正好,线程池缓存线程,可用已有的闲置线程来执行新任务,避免了T1+T3带来的系统开销

2、阻塞

线程并发数量过多,抢占系统资源从而导致阻塞

我们知道线程能共享系统资源,如果同时执行的线程过多,就有可能导致系统资源不足而产生阻塞的情况 运用线程池能有效的控制线程最大并发数,避免以上的问题

3、对线程进行一些简单的管理

比如:延时执行、定时循环执行的策略等,运用线程池都能进行很好的实现

四、线程池的优势

在业务场景中, 如果一个对象创建销毁开销比较大, 那么此时建议池化对象进行管理。

例如线程,jdbc连接等等, 在高并发场景中, 如果可以复用之前销毁的对象, 那么系统效率将大大提升。

另外一个好处是可以设定池化对象的上限, 例如预防创建线程数量过多导致系统崩溃的场景。

五、线程池类的主要参数

  • corePoolSize:线程池的核心大小,也可以理解为最小的线程池大小。
  • maximumPoolSize:最大线程池大小。
  • keepAliveTime:空余线程存活时间,指的是超过corePoolSize的空余线程达到多长时间才进行销毁。
  • unit:销毁时间单位。
  • workQueue:存储等待执行线程的工作队列。
  • threadFactory:创建线程的工厂,一般用默认即可。
  • handler:拒绝策略,当工作队列、线程池全已满时如何拒绝新任务,默认抛出异常。

六、线程池的工作原理

如果线程池中的线程小于corePoolSize时就会创建新线程直接执行任务。

如果线程池中的线程大于corePoolSize时就会暂时把任务存储到工作队列workQueue中等待执行。

如果工作队列workQueue也满时,当线程数小于最大线程池数maximumPoolSize时就会创建新线程来处理,而线程数大于等于最大线程池数maximumPoolSize时就会执行拒绝策略。

一般流程图如下

在这里插入图片描述

七、线程池大小设置

配置线程池的大小可根据以下几个维度进行分析来配置合理的线程数:

  • 任务性质可分为:CPU密集型任务,IO密集型任务,混合型任务。
  • 任务的执行时长。
  • 任务是否有依赖——依赖其他系统资源,如数据库连接等。

1、CPU密集型任务

尽量使用较小的线程池,一般为CPU核数+1。 因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,只能增加上下文切换的次数,因此会带来额外的开销。

2、IO密集型任务

可以使用稍大的线程池,一般为2*CPU核数+1。 因为IO操作不占用CPU,不要让CPU闲下来,应加大线程数量,因此可以让CPU在等待IO的时候去处理别的任务,充分利用CPU时间。

3、混合型任务

可以将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池去处理。 只要分完之后两个任务的执行时间相差不大,那么就会比串行执行来的高效。 因为如果划分之后两个任务执行时间相差甚远,那么先执行完的任务就要等后执行完的任务,最终的时间仍然取决于后执行完的任务,而且还要加上任务拆分与合并的开销,得不偿失

4、依赖其他资源

如某个任务依赖数据库的连接返回的结果,这时候等待的时间越长,则CPU空闲的时间越长,那么线程数量应设置得越大,才能更好的利用CPU。 借鉴别人的文章 对线程池大小的估算公式:

最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目

比如平均每个线程CPU运行时间为0.5s,而线程等待时间(非CPU运行时间,比如IO)为1.5s,CPU核心数为8,那么根据上面这个公式估算得到:((0.5+1.5)/0.5)*8=32。

可以得出一个结论: 线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。

但是通过公式计算出来的线程池大小也只是参考,准确的线程池设置大小还是需要经过性能测试验证获得的。

八、线程池监控

通过工具类监控线程池的使用、状态等,例如通过JDK自带的Jconsole、Jvisualvm监控

1)首先我们先手动创建一个简单的死锁程序并运行

在这里插入图片描述

2)使用JDK自带的Jconsole进行监控,选择text进程并连接

在这里插入图片描述

3)点击检测死锁,选择线程并查看信息,可以定位到java代码发生死锁的位置

在这里插入图片描述

在这里插入图片描述

4)使用JAVA自带的jvisualvm进行监控,选择text进程

在这里插入图片描述

5)选择线程TAB,查看线程信息,如果存在死锁会出现下图的样式

在这里插入图片描述

6)点击线程Dump之后进入新页面拖到最底下就能看到线程死锁详细信息

在这里插入图片描述

另外除了监控线程死锁,还可以通过线程池提供的以下参数对线程池进行监控。

  • taskCount:线程池需要执行的任务数量,包括已经执行完的、未执行的和正在执行的。
  • completedTaskCount:线程池在运行过程中已完成的任务数量,completedTaskCount <= taskCount。
  • largestPoolSize:线程池曾经创建过的最大线程数量,通过这个数据可以知道线程池是否满过。如等于线程池的最大大小,则表示线程池曾经满了。
  • getPoolSize: 线程池的线程数量。如果线程池不销毁的话,池里的线程不会自动销毁,所以线程池的线程数量只增不减。
  • getActiveCount:获取活动的线程数。

九、总结

对于监控而言,不在于手段的多样性,而需要明白监控的本质,以及需要的监控项内容,找出系统瓶颈,规避风险。

到此这篇关于深入了解Java线程池的原理和监控的文章就介绍到这了,更多相关Java线程池原理和监控内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 使用IDEA如何打包发布SpringBoot并部署到云服务器

    使用IDEA如何打包发布SpringBoot并部署到云服务器

    这篇文章主要介绍了使用IDEA如何打包发布SpringBoot并部署到云服务器问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12
  • 无感NullPointerException的值相等判断方法

    无感NullPointerException的值相等判断方法

    当我们需要去判断一个 入参/查库 返回的开关变量(通常是个Integer类型的)时,常常会写如下的if-else判断语句。但又会为在生产环境看到的「NullPointerException」感到困扰,遇到这个问题如何处理呢,下面小编通过本文给大家详细讲解,需要的朋友参考下吧
    2023-02-02
  • Java实现将PDF转为PDF/A

    Java实现将PDF转为PDF/A

    通过将PDF格式转换为PDF/A格式,可保护文档布局、格式、字体、大小等不受更改,从而实现文档安全保护的目的,同时又能保证文档可读、可访问。本文将为大家介绍如何实现这一转换,需要的可以参考一下
    2022-01-01
  • java处理转义字符↑ → ↓ 保存后的展示还原操作

    java处理转义字符↑ → ↓ 保存后的展示还原操作

    这篇文章主要介绍了java处理转义字符↑ → ↓ 保存后的展示还原操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • Spring Security学习之rememberMe自动登录的实现

    Spring Security学习之rememberMe自动登录的实现

    这篇文章主要给大家介绍了关于Spring Security学习之rememberMe自动登录的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2020-06-06
  • SpringBoot集成JWT生成token及校验方法过程解析

    SpringBoot集成JWT生成token及校验方法过程解析

    这篇文章主要介绍了SpringBoot集成JWT生成token及校验方法过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • Mybatis的xml文件时间范围条件查询方式

    Mybatis的xml文件时间范围条件查询方式

    这篇文章主要介绍了Mybatis的xml文件时间范围条件查询方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • 探讨Java验证码制作(下篇)

    探讨Java验证码制作(下篇)

    这篇文章主要介绍了探讨Java验证码制作(下篇)的相关资料,需要的朋友可以参考下
    2016-05-05
  • Java设计模式之工厂模式

    Java设计模式之工厂模式

    这篇文章主要为大家详细介绍了Java设计模式之工厂模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03
  • Java线程的创建介绍及实现方式示例

    Java线程的创建介绍及实现方式示例

    这篇文章主要为大家介绍了Java线程的创建介绍及实现方式示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09

最新评论