深入了解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线程池原理和监控内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Maven 插件配置分层架构深度解析

    Maven 插件配置分层架构深度解析

    这篇文章主要介绍了Maven 插件配置分层架构深度解析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2025-05-05
  • Java创建对象的四种方式详解

    Java创建对象的四种方式详解

    这篇文章主要介绍了Java创建对象的四种方式详解,如果我们不想利用默认构造器来创建java对象,而想利用指定的构造器来创建java对象,则需要利用Construtor对象,每个Construtor对应一个构造器,需要的朋友可以参考下
    2023-11-11
  • NetBeans安装提示neatbeans cannot find java 1.8 or higher

    NetBeans安装提示neatbeans cannot find java 1.8 or higher

    今天小编就为大家分享一篇关于NetBeans安装提示neatbeans cannot find java 1.8 or higher,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-04-04
  • SpringBoot集成Tess4J实现OCR的示例代码

    SpringBoot集成Tess4J实现OCR的示例代码

    Tess4J是一个基于Tesseract OCR引擎的Java接口,可以用来识别图像中的文本,说白了,就是封装了它的API,让Java可以直接调用,本文给大家介绍了SpringBoot集成Tess4J实现OCR的示例,需要的朋友可以参考下
    2024-12-12
  • SpringBoot使用Spark过程详解

    SpringBoot使用Spark过程详解

    这篇文章主要介绍SpringBoot使用Spark的方法的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望能帮助大家解决问题
    2023-02-02
  • java基础之注解示例详解

    java基础之注解示例详解

    大家好,本篇文章主要讲的是java基础之注解示例详解,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • 浅谈JAVA设计模式之享元模式

    浅谈JAVA设计模式之享元模式

    这篇文章主要介绍了JAVA设计模式之享元模式的的相关资料,文中详细的介绍了享元模式的概念以及使用方法,感兴趣的朋友可以了解下
    2020-06-06
  • Java中的Sentinel规则持久化详解

    Java中的Sentinel规则持久化详解

    这篇文章主要介绍了Java中的Sentinel规则持久化详解,将限流配置规则持久化进Nacos保存,只要刷新8401某个rest地址,sentinel控制台的流控规则就能看到,只要Nacos里面的配置不删除,针对8401上sentinel上的流控规则持续有效,需要的朋友可以参考下
    2023-09-09
  • java 实现单链表逆转详解及实例代码

    java 实现单链表逆转详解及实例代码

    这篇文章主要介绍了java 实现单链表逆转实例代码的相关资料,需要的朋友可以参考下
    2017-02-02
  • Spring中如何使用@Value注解实现给Bean属性赋值

    Spring中如何使用@Value注解实现给Bean属性赋值

    这篇文章主要介绍了Spring中如何使用@Value注解实现给Bean属性赋值的操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08

最新评论