ScheduledThreadPoolExecutor原理探究

这是一个可以在指定一定延迟时间后或者定时进行任务调度执行的线程池。

介绍

Executors其实是个工具类,它提供了好多静态方法,可根据用户的选择返回不同的线程池实例。

ScheduledThreadPoolExecutor继承了ThreadPoolExecutor并实现了ScheduledExecutorService接口。

线程池队列是DelayedWorkQueue,其和DelayedQueue类似,是一个延迟队列。

ScheduledFutureTask是具有返回值的任务,继承自FutureTask。

FutureTask的内部有一个变量state用来表示任务的状态,一开始状态为NEW,所有状态为。

在这里插入图片描述
可能的任务状态转换路径为
在这里插入图片描述
ScheduledFutureTask内部还有一个变量period用来表示任务的类型,任务类型如下:

  • period=0,说明当前任务是一次性的,执行完毕后就退出了。
  • period为负数,说明当前任务为fixed-delay任务,是固定延迟的定时可重复执行任务。
  • period为正数,说明当前任务为fixed-rate任务,是固定频率的定时可重复执行任务。

ScheduledThreadPoolExecutor的一个构造函数如下,由该构造函数可知线程池队列是DelayedWorkQueue。

在这里插入图片描述

原理剖析

schedule(Runnable command,long delay,TimeUnit unit)方法

该方法的作用是提交一个延迟执行的任务,任务从提交时间算起延迟单位为unit的delay时间后开始执行。

提交的任务不是周期性任务,任务只会执行一次,代码如下。

在这里插入图片描述
代码(2)装饰任务,把提交的command任务转换为ScheduledFutureTask。

ScheduledFutureTask是具体放入延迟队列里面的东西。

由于是延迟任务,所以ScheduledFutureTask实现了long getDelay(TimeUnit unit)和int compareTo(Delayed other)方法。

triggerTime方法将延迟时间转换为绝对时间,也就是把当前时间的纳秒数加上延迟的纳秒数后的long型值。

ScheduledFutureTask的构造函数如下。

在这里插入图片描述
在构造函数内部首先调用了父类FutureTask的构造函数,父类FutureTask的构造函数代码如下。

在这里插入图片描述
compareTo(Delayed other)方法的代码如下:
在这里插入图片描述
compareTo的作用是加入元素到延迟队列后,在内部建立或者调整堆时会使用该元素的compareTo方法与队列里面其他元素进行比较,让最快要过期的元素放到队首。

所以无论什么时候向队列里面添加元素,队首的元素都是最快要过期的元素。

代码(3)将任务添加到延迟队列,delayedExecute的代码如下。

在这里插入图片描述
代码(4)首先判断当前线程池是否已经关闭了,如果已经关闭则执行线程池的拒绝策略,否则执行代码(5)将任务添加到延迟队列。

添加完毕后还要重新检查线程池是否被关闭了,如果已经关闭则从延迟队列里面删除刚才添加的任务,但是此时有可能线程池中的线程已经从任务队列里面移除了该任务,也就是该任务已经在执行了,所以还需要调用任务的cancle方法取消任务。

如果代码(6)判断结果为false,则会执行代码(7)确保至少有一个线程在处理任务,即使核心线程数corePoolSize被设置为0。

ensurePrestart的代码如下。

在这里插入图片描述
上面我们分析了如何向延迟队列添加任务,下面我们来看线程池里面的线程如何获取并执行任务。

在前面讲解ThreadPoolExecutor时我们说过,具体执行任务的线程是Worker线程,Worker线程调用具体任务的run方法来执行。

由于这里的任务是ScheduledFutureTask,所以我们下面看看ScheduledFutureTask的run方法。

在这里插入图片描述
代码(8)中的isPeriodic的作用是判断当前任务是一次性任务还是可重复执行的任务,isPeriodic的代码如下。

在这里插入图片描述
代码(9)判断当前任务是否应该被取消,canRunInCurrentRunState的代码如下。

在这里插入图片描述
这里传递的periodic的值为false,所以isRunningOrShutdown的参数为executeExistingDelayedTasksAfterShutdown。

executeExistingDelayedTasksAfterShutdown默认为true,表示当其他线程调用了shutdown命令关闭了线程池后,当前任务还是要执行,否则如果为false,则当前任务要被取消。

由于periodic的值为false,所以执行代码(10)调用父类FutureTask的run方法具体执行任务。FutureTask的run方法的代码如下。在这里插入图片描述
如果任务执行成功则执行代码(13.2)修改任务状态,set方法的代码如下。

在这里插入图片描述
如上代码首先使用CAS将当前任务的状态从NEW转换到COMPLETING。

这里当有多个线程调用时只有一个线程会成功。

成功的线程再通过UNSAFE.putOrderedInt设置任务的状态为正常结束状态,这里没有使用CAS是因为对于同一个任务只可能有一个线程运行到这里。

在这里使用putOrderedInt比使用CAS或者putLongvolatile效率要高,并且这里的场景不要求其他线程马上对设置的状态值可见。

请思考个问题,在什么时候多个线程会同时执行CAS将当前任务的状态从NEW转换到COMPLETING?其实当同一个command被多次提交到线程池时就会存在这样的情况,因为同一个任务共享一个状态值state。

如果任务执行失败,则执行代码(13.1)。setException的代码如下,可见与set函数类似。

在这里插入图片描述

scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit)

该方法的作用是,当任务执行完毕后,让其延迟固定时间后再次运行(fixed-delay任务)。

其中initialDelay表示提交任务后延迟多少时间开始执行任务command,delay表示当任务执行完毕后延长多少时间后再次运行command任务,unit是initialDelay和delay的时间单位。

任务会一直重复运行直到任务运行中抛出了异常,被取消了,或者关闭了线程池。

scheduleWithFixedDelay的代码如下。

在这里插入图片描述
将任务添加到延迟队列后线程池线程会从队列里面获取任务,然后调用ScheduledFutureTask的run方法执行。

由于这里period<0,所以isPeriodic返回true,所以执行代码(11)。runAndReset的代码如下。

在这里插入图片描述
该代码和FutureTask的run方法类似,只是任务正常执行完毕后不会设置任务的状态,这样做是为了让任务成为可重复执行的任务。

这里多了代码(19),这段代码判断如果当前任务正常执行完毕并且任务状态为NEW则返回true,否则返回false。

如果返回了true则执行代码(11.1)的setNextRunTime方法设置该任务下一次的执行时间。setNextRunTime的代码如下。

在这里插入图片描述

scheduleAtFixedRate(Runnable command,long initialDelay,longperiod,TimeUnit unit)

该方法相对起始时间点以固定频率调用指定的任务(fixed-rate任务)。

当把任务提交到线程池并延迟initialDelay时间(时间单位为unit)后开始执行任务command。

然后从initialDelay+period时间点再次执行,而后在initialDelay+2*period时间点再次执行,循环往复,直到抛出异常或者调用了任务的cancel方法取消了任务,或者关闭了线程池。

scheduleAtFixedRate的原理与scheduleWithFixedDelay类似,下面我们讲下它们之间的不同点。

首先调用scheduleAtFixedRate的代码如下。

在这里插入图片描述
在这里插入图片描述

  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ScheduledThreadPoolExecutorJava 中的一个线程池实现,它可以根据指定的延迟时间或定时周期执行任务。其原理主要包括以下几个方面: 1. 线程池管理:ScheduledThreadPoolExecutor 内部维护一个线程池,其中包含多个工作线程。这些工作线程可以并行执行提交的任务。 2. 任务队列:ScheduledThreadPoolExecutor 使用一个任务队列来存储提交的任务。当任务被提交时,会先进入任务队列中等待执行。 3. 任务调度:ScheduledThreadPoolExecutor 使用一个调度器来控制任务的执行时间。调度器会根据任务的延迟时间或定时周期,将任务从任务队列中取出,并分配给空闲的工作线程执行。 4. 线程池调度策略:ScheduledThreadPoolExecutor 提供了不同的调度策略,可以根据需要选择合适的策略。见的调度策略包括延迟执行、周期性执行、固定延迟执行等。 5. 线程池管理和任务调度的协调:ScheduledThreadPoolExecutor 通过线程池管理和任务调度的协调,实现了对延迟执行或定时周期执行任务的支持。它会根据需要动态地创建、销毁工作线程,并将任务分配给这些工作线程执行。 总之,ScheduledThreadPoolExecutor 通过线程池管理和任务调度的机制,实现了对延迟执行或定时周期执行任务的支持。这使得开发者可以方便地控制任务的执行时间,并且可以充分利用系统资源来提高任务执行的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值