对于线程池,从全局视角来看,有两个基本点:
- 线程的数量
- 阻塞队列
ScheduledThreadPoolExecutor的线程数量:
如果corePoolSize大于0,那么线程数量最终就是corePoolSize,都是核心线程,没有非核心线程,maximumPoolSize形同虚设。
如果corePoolSize等于0,只会创建1个非核心线程。
ScheduledThreadPoolExecutor的阻塞队列:
基于堆实现的延迟队列,按任务的执行时间(纳秒级)排序。
任务如何定时执行的?
使用ScheduledFutureTask对提交的Runnable任务以及初始延迟和周期时间做一层封装。
封装后的run方法,在执行完提交的任务后,重置任务的下次执行时间,然后重新入队,等待下次执行,周而复始。
封装后的run方法scheduleAtFixedRate和scheduleWithFixedDelay的区别
区别在于setNextRunTime。
setNextRunTimetriggerTime方法:
long triggerTime(long delay) {
return System.nanoTime() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
scheduleWithFixedDelay在任务执行完后,依据当时的纳秒时间点,再加上调度周期作为任务下次执行的期望时间点。而scheduleAtFixedRate则不管怎样,都是在上次期望任务执行的时间点上+调度周期作为下次执行的期望时间点。
初始延迟(initialDelay)参数的意义
防止大量执行时间相近的定时任务撞到一起,任务执行时,线程执行不过来,导致挤在后面的定时任务不能按预期的执行周期执行。
建议最好随机化这个初始延迟时间,降低任务撞在一起的概率。尤其是scheduleAtFixedRate调度的任务,一旦撞了就解不了套了。
网友评论