【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/
转载文章受原作者版权保护。转载请注明原作者出处!