简介
上一篇文章剖析了ThreadPoolExecutor的原理和实现,这里剖析下ScheduledThreadPoolExecutor的实现方式。ScheduledThreadPoolExecuto能够执行延迟固定时间的任务和周期性任务。在需要多工作线程的情况下,它比Timer更方便,同时比ThreadPoolExecutor有更大灵活性和能力。
但是在使用ScheduledThreadPoolExecutor时,有几个点必须注意:
1.在执行延迟任务时,任务的执行不会早于你设定的延迟时间,但是也不保证恰好在达到你设定的延迟时间时就马上执行。在同一时间开始执行的任务,按照FIFO顺序提交给线程。
2.虽然ScheduledThreadPoolExecutor继承了ThreadPoolExecutor。但是针对ThreadPoolExecutor的一些性能调优的方法不一定试用于ScheduledThreadPoolExecutor。在ThreadPoolExecutor中,调整corePoolSize,maximumPoolSize两个参数可以控制任务的执行速度,例如当前线程小于corePoolSize时,可以马上为新任务创建线程而不必等待,当前线程大于corePoolSize小于maximumPoolSize。可以动态扩展当前线程以提高并发速度等。但是对于ScheduledThreadPoolExecutor,它实际上是一个固定线程数,无界阻塞队列的线程池,所有任务都是先放到工作队列中,而不会有直接为其创建线程的优待。所以调整corePoolSize,maximumPoolSize对ScheduledThreadPoolExecutor来说,性能上不会有影响。
实现原理
ScheduledThreadPoolExecutor继承了ThreadPoolExecutor,它先把用户提交的任务进一步封装成ScheduledFutureTask。再把ScheduledFutureTask放到任务队列中,交给线程池的线程去执行。可以说ScheduledThreadPoolExecutor与ThreadPoolExecutor的最大区别就是多了一步封装ScheduledFutureTask的过程。其他像任务队列的执行,线程的管理等都于ThreadPoolExecutor差不多,甚至就是直接使用了ThreadPoolExecutor的方法。下面就主要研究下ScheduledThreadPoolExecutor与ThreadPoolExecutor的不同地方。与ThreadPoolExecutor相关的这里直接省略,需要参考ThreadPoolExecutor的地方,请移步ThreadPoolExecutor的原理和实现。
ScheduledFutureTask的设计
把用户任务进一步封装成ScheduledFutureTask是ScheduledThreadPoolExecutor与ThreadPoolExecutor的主要区别。因此ScheduledFutureTask的设计就是ScheduledThreadPoolExecutor的核心。ScheduledFutureTask是ScheduledThreadPoolExecutor的一个内部类。ScheduledFutureTask继承了FutureTask。先看下ScheduledFutureTask独有的属性:
属性 | 属性描述 |
long sequenceNumber | 跳出FIFO调度关系的序列号 |
long time | 激活任务的时间,单位是纳秒。注意:这个时间不是需要延迟的时间。而是用纳秒表示的自然时间。 |
long period | 周期性任务的时间间隔,单位是纳秒。正数表示固定频率,负数表示固定的延迟时间。 |
ScheduledFutureTask提供了三种构造方法:
1.ScheduledFutureTask(Runnable r, V result, long ns)。 创建设定了任务执行的触发时间的一次性任务
1.ScheduledFutureTask(Runnable r, V result, long ns, long period)。创建设定了任务触发时间和周期时间的周期性任务。
2.ScheduledFutureTask(Callable<V> callable, long ns)。创建Callable,固定触发时间的一次性任务。
ScheduledFutureTask继承自FutureTask,所以需要实现自己的run方法:
public void run() { if (isPeriodic()) runPeriodic(); else ScheduledFutureTask.super.run(); }
如果是一次性任务,则执行FutureTask的run方法,如果是周期性任务,则执行runPeriodic()方法。下面看下runPeriodic()做了什么事情:
private void runPeriodic() { boolean ok = ScheduledFutureTask.super.runAndReset(); boolean down = isShutdown(); // Reschedule if not cancelled and not shutdown or policy allows if (ok && (!down || (getContinueExistingPeriodicTasksAfterShutdownPolicy() && !isStopped()))) { long p = period; if (p > 0) time += p; else time = triggerTime(-p); ScheduledThreadPoolExecutor.super.getQueue().add(this); } // This might have been the final executed delayed // task. Wake up threads to check. else if (down) interruptIdleWorkers(); }
首先,先执行FutureTask的runAndReset()。这个方法尝试执行一次用户任务,重置状态,并返回是否成功执行。判断下状态:任务成功执行了,线程池没有shutdown,任务没有被取消。那么设置任务的下一次执行时间。这里设置下次执行时间比较有意思如果period>0则time=time+p,若果period<=0,time=now()+p。即前者是上次任务开始的时间加上时间周期,后置是上一次任务执行完成后的时间加上时间周期。这样说有点拗口,换种方式来说:当把时间周期设置为正数,那么表示该任务每多长时间执行一次,任务的执行频率是固定的。如果间周期设置为负数,表示前一次任务执行完之后间隔多长时间开始执行下一次任务,每个任务之间的延时是固定的。
ScheduledThreadPoolExecutor的实现
ScheduledThreadPoolExecutor继承了ThreadPoolExecutor并且实现了ScheduledExecutorService接口。
ScheduledExecutorService定义了使用ScheduledThreadPoolExecutor提交任务的接口schedule。schedule是一组返回值和参数不同的overwrite接口。以供用户选择提交任务的模式,比如是一次性的定时任务还是周期性的任务,需不需要返回结果等等。不管是哪个schedule接口,他的实现功能主要是把用户提交的任务和模式封装成ScheduledFutureTask,然后调用delayedExecute(Runnable command)。所以delayedExecute方法是真正的处理任务的方法,代码如下:
private void delayedExecute(Runnable command) { if (isShutdown()) { reject(command); return; } // Prestart a thread if necessary. We cannot prestart it // running the task because the task (probably) shouldn't be // run yet, so thread will just idle until delay elapses. if (getPoolSize() < getCorePoolSize()) prestartCoreThread(); super.getQueue().add(command); }
如果线程池shutdown了,则执行reject策略。如果当前线程小于核心线程,那么调用prestartCoreThread()创建一个线程线程。prestartCoreThread()实际上是调用了ThreadPoolExecutor类里的addIfUnderCorePoolSize(null)。如果你看了ThreadPoolExecutor的分析。就会知道这个addIfUnderCorePoolSize(null)的参数设置为null其实就是只创建线程,而不会把当前任务直接提交给这个线程。任务直接通过super.getQueue().add(command);放到任务队列里去了。这也解释了前面简介中第2点说了为什么调整corePoolSize也不会让任务马上执行了,不管你corePoolSize调多大,你的任务还是需要排队,只是你的线程多了,可能任务排队等待的时间少了。
另外这里也可以解释简介中第1点注意事项:对于定时任务,只能保证执行时间不会早于你设定的时间。但不一定会恰好在你设定的时间点上执行。因为你提交的任务都是排队的。加入你12点00分00秒提交了一个任务,希望这个任务在12点00分05秒的时刻执行。加入你的线程任务处理的足够快,在12点00分03秒的时候这个任务得到了一个线程,那么这个线程开始执行你的任务,发现你的执行时间是12点00分05秒,那么线程会等2秒到12点00分05秒的时候开始执行你的任务;但是如果线程池线程数比较少或者任务执行比较耗时等,可能使你的任务在12点00分10秒的时刻才分配到了一个线程,然后线程发现你的任务已经过了到了执行时间,马上开始执行你的任务,这种情况下,相当于任务比你设定的时间延迟了5秒。
相关推荐
5. **线程池**:理解ExecutorService、ThreadPoolExecutor、ScheduledThreadPoolExecutor的工作原理,优化线程池配置,避免资源浪费。 6. **原子类**:熟悉AtomicInteger、AtomicLong、AtomicReference等原子类,...
少儿编程scratch项目源代码文件案例素材-绝地求生.zip
嵌入式八股文面试题库资料知识宝典-文思创新面试题2010-04-08.zip
一种基于剪切波和特征信息检测的太阳斑点图融合算法.pdf
内容概要:本文详细介绍了并联型有源电力滤波器(APF)在Matlab/Simulink环境下的仿真研究。主要内容涵盖三个关键技术点:一是dq与αβ坐标系下的谐波和无功检测,利用dq变换和FBD技术实现实时检测;二是两相旋转坐标系(dq)与两相静止坐标系(αβ)下的PI控制,通过调整比例和积分环节实现精准控制;三是SVPWM调制方式的应用,通过优化开关时序提升系统效率和性能。文中还提供了详细的仿真介绍文档,包括模型搭建、参数设定以及结果分析。 适合人群:从事电力电子、自动化控制领域的研究人员和技术人员,尤其是对电力滤波器仿真感兴趣的读者。 使用场景及目标:适用于需要深入了解并联型APF工作原理和实现方式的研究人员,旨在通过仿真工具掌握谐波和无功检测、PI控制及SVPWM调制的具体应用。 其他说明:本文不仅提供了理论知识,还结合了实际操作步骤,使读者能够通过仿真模型加深对APF的理解。
Arduino KEY实验例程,开发板:正点原子EPS32S3,本人主页有详细实验说明可供参考。
嵌入式八股文面试题库资料知识宝典-嵌入式C语言面试题汇总(66页带答案).zip
.archivetempdebug.zip
嵌入式系统开发_CH551单片机_USB_HID复合设备模拟_基于CH551单片机的USB键盘鼠标复合设备模拟器项目_用于通过CH551微控制器模拟USB键盘和鼠标输入设备_实现硬
少儿编程scratch项目源代码文件案例素材-剑客冲刺.zip
少儿编程scratch项目源代码文件案例素材-火影.zip
内容概要:本文详细介绍了两极式单相光伏并网系统的组成及其仿真优化方法。前级采用Boost电路结合扰动观察法(P&O)进行最大功率点跟踪(MPPT),将光伏板输出电压提升至并网所需水平;后级利用全桥逆变加L型滤波以及电压外环电流内环控制,确保并网电流与电网电压同频同相,实现高效稳定的并网传输。文中还提供了具体的仿真技巧,如开关频率设置、L滤波参数计算和并网瞬间软启动等,最终实现了98.2%的系统效率和低于0.39%的总谐波失真率(THD)。 适合人群:从事光伏并网系统研究、设计和开发的技术人员,特别是对Boost电路、MPPT算法、逆变技术和双环控制系统感兴趣的工程师。 使用场景及目标:适用于希望深入了解两极式单相光伏并网系统的工作原理和技术细节的研究人员和工程师。目标是在实际项目中应用这些理论和技术,提高光伏并网系统的效率和稳定性。 其他说明:文中提供的仿真技巧和伪代码有助于读者更好地理解和实现相关算法,在实践中不断优化系统性能。同时,注意电网电压跌落时快速切换到孤岛模式的需求,确保系统的安全性和可靠性。
矢量边界,行政区域边界,精确到乡镇街道,可直接导入arcgis使用
嵌入式八股文面试题库资料知识宝典-嵌入式c面试.zip
嵌入式八股文面试题库资料知识宝典-I2C总线.zip
内容概要:本文详细介绍了三种注浆模型——随机裂隙网络注浆模型、基于两相达西定律的注浆模型、基于层流和水平集的注浆扩散模型。首先,随机裂隙网络注浆模型基于地质学原理,模拟裂隙网络发育的实际地质情况,在不同注浆压力下进行注浆作业,以增强地基稳定性和提高承载能力。其次,基于两相达西定律的注浆模型利用数学公式模拟裂隙网络中的流体输送过程,适用于裂隙网络地质条件下的注浆效果分析。最后,基于层流和水平集的注浆扩散模型通过引入层流特性和水平集方法,更准确地模拟注浆过程中的扩散过程。文中还讨论了不同注浆压力对注浆效果的影响,并提出了优化建议。 适合人群:从事岩土工程、地基加固等相关领域的工程师和技术人员。 使用场景及目标:①帮助工程师选择合适的注浆模型和注浆压力;②为实际工程项目提供理论支持和技术指导;③提升地基加固的效果和效率。 其他说明:文章强调了在实际应用中需要结合地质条件、裂隙网络特点等因素进行综合分析,以达到最佳注浆效果。同时,鼓励不断创新注浆工艺和方法,以满足日益增长的地基加固需求。
内容概要:本文详细比较了COMSOL Multiphysics软件5.5和6.0版本在模拟Ar棒板粗通道流注放电现象方面的异同。重点探讨了不同版本在处理电子密度、电子温度、电场强度以及三维视图等方面的优缺点。文中不仅介绍了各版本特有的操作方式和技术特点,还提供了具体的代码实例来展示如何进行精确的仿真设置。此外,文章还讨论了网格划分、三维数据提取和电场强度后处理等方面的技术难点及其解决方案。 适合人群:从事等离子体物理研究的专业人士,尤其是熟悉COMSOL Multiphysics软件并希望深入了解其最新特性的研究人员。 使用场景及目标:帮助用户选择合适的COMSOL版本进行高效、精确的等离子体仿真研究,特别是在处理复杂的Ar棒板粗通道流注放电现象时提供指导。 其他说明:文章强调了在实际应用中,选择COMSOL版本不仅要考虑便捷性和视觉效果,还需兼顾仿真精度和可控性。
嵌入式八股文面试题库资料知识宝典-C and C++ normal interview_8.doc.zip
内容概要:本文详细介绍了在现代通信系统中,抗干扰技术的重要性和具体应用方法。首先阐述了抗干扰技术的背景及其重要性,随后分别讨论了捷变频技术和波形优化技术的具体机制和优势。捷变频技术能快速改变工作频率,防止被干扰源锁定;波形优化技术则通过改进信号波形来提升抗干扰性能。接着,文章探讨了两种技术相结合的协同效应,最后重点介绍了发射信号及接收滤波器联合优化的抗干扰策略(ISRJ),这是一种综合性优化手段,旨在最大化抗干扰效果并提高通信质量。 适合人群:从事通信工程及相关领域的研究人员和技术人员,尤其是关注抗干扰技术的专业人士。 使用场景及目标:适用于需要提升通信系统稳定性和可靠性的场合,如军事通信、卫星通信等领域。目标是帮助技术人员理解和掌握先进的抗干扰技术,应用于实际项目中。 其他说明:文中提到的技术不仅限于理论层面,还涉及具体的实施细节和应用场景,有助于读者深入理解并应用于实践中。