线程池底层原理详解与源码分析(补充部分—ScheduledThreadPoolExecutor类分析)

【1】前言

【2】ScheduledThreadPoolExecutor的介绍

1.ScheduledThreadPoolExecutor 继承自ThreadPoolExecutor。它主要用来在给定的延迟之后运行任务,或者定期执行任务。ScheduledThreadPoolExecutor可以在构造函数中指定多个对应的后台线程数。

2.构造函数展示

3.通过构造函数我们可以看到,它的线程池本身就是调用ThreadPoolExecutor类的构造方法,因此也继承了ThreadPoolExecutor类所存在的隐患:

允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。(且CPU会变成100%)

4.PS:既然隐患这么严重,使用原生的不太合适。正所谓,人无横财不富,马无夜草不肥,打不过就加入。ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,那就写个类继承它然后调用ThreadPoolExecutor的构造方法区解决掉创建线程数被写死为最大值的情况,然后了解一下DelayedWorkQueue(这个本质上也是优先级队列),继承一下也改写吧。毕竟自己的最合适不是吗。【毕竟我觉得这些都是大佬们留给菜鸡的底版,如拒绝策略不也是四个默认都没人用吗,都是要你根据自己的场景改】(毕竟我这猜测的原因是因为有了无尽队列,其实线程数设置为Integer.MAX_VALUE已经没有意义了)

【3】ScheduledThreadPoolExecutor的使用

1)schedule(Runnable command, long delay, TimeUnit unit)

方法说明:无返回值的延迟任务,有个严重的问题,就是没有办法获知task的执行结果
2)schedule(Callable callable, long delay, TimeUnit unit)

方法说明:有返回值的延迟任务 :接收的是Callable实例,会返回一个ScheduleFuture对象,通过ScheduleFuture可以取消一个未执行的task,也可以获得这个task的执行结果
3)scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)

方法说明: 固定频率周期任务:第一次执行的延迟根据initialDelay参数确定,以后每一次执行都间隔period时长
4)scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)

方法说明: 固定延迟周期任务 :scheduleWithFixedDelay的参数和scheduleAtFixedRate参数完全一致,它们的不同之处在于对period调度周期的解释。在scheduleAtFixedRate中,period指的两个任务开始执行的时间间隔,也就是当前任务的开始执行时间和下个任务的开始执行时间之间的间隔。而在scheduleWithFixedDelay中,period指的当前任务的结束执行时间到下个任务的开始执行时间。

【4】任务ScheduledFutureTask类源码分析

1.构造方法展示

代码展示

代码说明

1.三个标注的参数是任务中主要的成员变量。

2.其次,我们会发现callable的任务是没有间隔周期的:因为callable本身就是阻塞等待,而且周期性的也不合适。

3.实现了RunnableScheduledFuture接口,其主要方法isPeriodic()用于判断是不是周期任务,又继承了RunnableFuture接口.

4.ScheduledFutureTask又继承了FutureTask类,而FutureTask类实现了RunnableFuture接口。(故感觉RunnableFuture接口的那些方法挺重要的)

5.RunnableFuture接口主要是由Runnable和Future两大接口组成(自己去看继承关系),主要有run()方法。

2.ScheduledFutureTask类#run方法

代码展示

代码说明

1.这里面很明显存在一个隐患,那就是没有捕捉异常,所以如果我们自定义的run()方法中如果没有捕捉异常的话,那么出现异常的时候我们容易两眼摸瞎。

2.故使用定时任务的时候,自定义的run方法需要自行捕捉异常进行处理。

3.ScheduledFutureTask类#setNextRunTime方法

代码展示

代码说明

1.周期时间period有正有负,这是ScheduledThreadPoolExecutor的ScheduledAtFixedRate和ScheduledWithFixedDelay的方法区别,前者为正数,后者为负数。
2.正数时,下一次执行时间为原来的执行时间+周期,即以执行开始时间为基准。
3.负数时,不考虑溢出情况,下一次执行时间为当前时间+周期,即以执行结束时间为基准。如果溢出,下一次执行时间为Long.MAX_VALUE + headDelay。

疑问说明(这一步有兴趣的需要自己去调试然后在核心方法处断点查看就可以了)

其实只要当做作System.nanoTime() + delay就可以了,没必要关注overflowFree这一步,原因:

1.如果执行了 Long.MAX_VALUE + headDelay ,triggerTime方法会获得负数,示例代码

2.如果不执行 Long.MAX_VALUE + headDelay ,triggerTime方法也有可能获得负数,示例代码:

3.而且获得负数在compareTo这一步不影响排序。【可能是由于科技发展的缘故吧,现在Long.MAX_VALUE【9223372036854775807L】溢出了,就会变为-9223372036854775808L,对排序不影响】

【5】ScheduledThreadPoolExecutor类源码分析

1.ScheduledThreadPoolExecutor的四种使用方法

2.ScheduledThreadPoolExecutor类#triggerTime方法

3.ScheduledThreadPoolExecutor类#delayedExecute方法

【6】DelayedWorkQueue类源码分析

0.DelayedWorkQueue类#核心属性

1.DelayedWorkQueue类#add方法

2.DelayedWorkQueue类#siftUp方法

3.ScheduledFutureTask类#compareTo方法

4.DelayedWorkQueue类#take方法

5.DelayedWorkQueue类#grow方法

6.DelayedWorkQueue类#remove方法

7.DelayedWorkQueue类#siftDown方法

8.DelayedWorkQueue类#finishPoll方法

Original: https://www.cnblogs.com/chafry/p/16734446.html
Author: 忧愁的chafry
Title: 线程池底层原理详解与源码分析(补充部分—ScheduledThreadPoolExecutor类分析)

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/622649/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 用两行代码实现重试功能,spring-retry真是简单而优雅

    最近做的一个需求,需要调用第三方接口。正常情况下,接口的响应是符合要求的,只有在网络抖动等极少数的情况下,会存在超时情况。因为是小概率事件,所以一次超时之后,进行一次重试操作应该就…

    Java 2023年6月5日
    081
  • Linux具体目录结构

    Linux具体目录结构 root : 该目录为系统管理员目录,root是具有超级权限的用户。 bin ->usr/bin : 存放系统预装的可执行程序,这里存放的可执行文件可…

    Java 2023年6月9日
    072
  • 分享两个实用的shell脚本

    各位,早上好啊~ 发现许久没有分享过技术文章了,今天分享两个部署项目时候比较实用的shell脚本 一键部署shell脚本 由于个人部署,会习惯把jar放到lib目录下面,如果你没有…

    Java 2023年6月13日
    082
  • Spring——注解@Bean和@Autowired的关系

    关系: @Bean 告诉 Spring:”这是这个类的一个实例,请保留它,并在我请求时将它还给我”。 @Autowired 说:”请给我一个这个…

    Java 2023年5月30日
    064
  • MongoDB 简介

    Failure will never overtake me if my determination to succeed is strong enough. 只要我成功的决心足够…

    Java 2023年6月9日
    065
  • 8.Hystrix隔离术

    Hystrix隔离之ThreadPoolKey Hystrix可以不填写ThreadPoolKey 默认Hystrix会使用GroupKey命名线程池 在Setting中加入and…

    Java 2023年6月8日
    092
  • 筛选两个集合不同数据

    undefined 谁在最外面,最后返回的集合就是谁的数据,例子中返回的集合数据是newSupplierBiznoDetail的不同数据List collect = newSupp…

    Java 2023年6月9日
    095
  • jdk1.8 stream 求和

    BigDecimal:BigDecimal bb =list.stream().map(Plan::getAmount).reduce(BigDecimal.ZERO,BigDec…

    Java 2023年5月30日
    051
  • Nginx(三)-正向代理与反向代理

    原文:正向代理与反向代理的区别 正向代理中,proxy和client同属一个LAN,对server透明; 反向代理中,proxy和server同属一个LAN,对client透明。 …

    Java 2023年5月30日
    077
  • 我也是醉了,Eureka 延迟注册还有这个坑!

    Eureka 有个延迟注册的功能,也就是在服务启动成功之后不立刻注册到 Eureka Server,而是延迟一段时间再去注册,这样做的主要目的是因为虽然服务启动成功了,可能还有一些…

    Java 2023年6月13日
    082
  • java中方法的重载和重写

    一、方法的重载(Overload) 概念: 重载(overloading) 是在一个类里面,方法名字相同,而参数列表(个数、类型、参数排列顺序等)不同。返回类型可以相同也可以不同。…

    Java 2023年6月13日
    061
  • session,model,request,servletContext

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月6日
    081
  • MaxComputer/ODPS使用pyODPS将计算值写入ODPS指定数据表中

    MaxComputer/ODPS使用pyODPS将计算值写入ODPS指定数据表中 阿里云官方参考文档:https://help.aliyun.com/document_detail…

    Java 2023年6月9日
    073
  • Spring Boot 读取配置文件

    Spring Boot 读取配置文件 Spring Boot 读取配置文件有两种方式: 加载多个 @ConfigurationProperties(xxx) 加载单个 @Value…

    Java 2023年6月5日
    061
  • SpringBoot集成文件-如何集成itextpdf导出PDF?itext的变迁?

    除了处理word, excel等文件外,最为常见的就是PDF的导出了。在java技术栈中,PDF创建和操作最为常用的itext了,但是使用itext一定要了解其版本历史和Licen…

    Java 2023年6月6日
    090
  • Mysql 安全加固经验总结

    本文为博主原创,转载请注明出处: 1.内网部署Mysql mysql 数据库在使用过程中,需要给服务提供连接和访问的权限,而不需要进行公网连接和访问,所以在安全环境和现网环境部署m…

    Java 2023年6月8日
    069
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球