本文主要介绍java定时器线程池(ScheduledThreadPoolExecutor),通过示例代码非常详细的介绍,对大家的学习或者工作有一定的参考价值。需要的朋友可以和边肖一起学习。
前言
计时器池提供了定期执行任务的能力,也就是说,它可以被延迟或定期执行。但是定时器线程池还是线程池,最底层的实现是ThreadPoolExecutor。可以参考我的另一篇文章《多线程——掌握ThreadPoolExecutor》。
特点说明
1.构造函数
public scheduled threadpoolexecutor(int corePoolSize){
//其他几个参数已经在ThreadPoolExecutor中详细分析过了,这里就不展开了。
//这里可以看到在基类中调用方法时有一个特殊的参数DelayedWorkQueue。
//同时我们也可以发现,这里并没有设置延迟时间、周期等参数。
//所以定时执行的实现必须在DelayedWorkQueue的对象中。
super(corePoolSize,整数。MAX_VALUE,0,纳秒,
new DelayedWorkQueue());
}
2.DelayedWorkQueue
DelayedWorkQueue是ScheduledThreadPoolExecutor中的内部类,它实现BlockingQueue接口。
存储任务队列的数组如下:
private RunnableScheduledFuture?[]队列=
新的RunnableScheduledFuture?[初始容量];
我们分析过ThreadPoolExecutor,它从任务队列中获取任务的方式是poll和take,所以看看poll和take的源代码,回头看,ThreadPoolExecutor会调用poll或take,先poll再take,只要其中一个接口返回。
public RunnableScheduledFuture?poll() {
final reentrant lock lock=this . lock;
lock . lock();
尝试{
RunnableScheduledFuture?first=queue[0];
//这里有一个getDelay,这是重点。获取执行延迟时间。
//但是如果我们有延迟设置,这将返回null,然后调用take方法
if (first==null || first.getDelay(纳秒)0)
返回null
其他
返回finish poll(first);
}最后{
lock . unlock();
}
}
public RunnableScheduledFuture?take()抛出InterruptedException {
final reentrant lock lock=this . lock;
lock . lock interruptible();
尝试{
for(;) {
RunnableScheduledFuture?first=queue[0];
if (first==null)
available . await();
否则{
//获取延迟时间
long delay=first.getDelay(纳秒);
if(延迟=0)
返回finish poll(first);
first=null//等待时不保留ref
如果(领导!=空)
available . await();
否则{
thread this thread=thread . current thread();
leader=thisThread
尝试{
//使用锁,并执行延迟等待。
//使用锁,并执行延迟等待。
//使用锁,并执行延迟等待。
available.awaitNanos(延迟);
}最后{
if (leader==thisThread)
leader=null
}
}
}
}
}最后{
if (leader==null queue[0]!=空)
available . signal();
lock . unlock();
}
}
3.RunnableScheduledFuture
在ScheduledThreadPoolExecutor中,有一个实现RunnableScheduledFuture的scheduledfutuetask类。scheduledfutuetask类采用了decorator设计模式,并在Runnable方法的基础上执行了一些附加功能。
我们需要特别注意几个参数,周期和时间。
(1)时间
先看时间的作用,可以看到时间是用来获取执行延迟时间的,也就是延迟是根据时间产生的。
公共long getDelay(时间单位单位){
return unit.convert(time - now()、纳秒);
}
(2)周期
这个参数不是说设置多少个执行周期,而是确定是否需要按周期执行,以及执行周期,即当前执行与下一次执行之间的时间。
//确定是否需要循环执行。如果循环设置为0,则不会无间隔执行,而只会执行一次。这需要特别注意。
public boolean isPeriodic() {
退货期!=0;
}
私有void setNextRunTime() {
长p=周期;
如果(第0页)
//这里把周期加到时间上,这样得到的延迟时间就是周期时间。
时间=p;
其他
time=trigger time(-p);
}
(3)执行
公共无效运行(){
//先确定是否是周期性任务。
布尔周期=is periodic();
如果(!canRunInCurrentRunState(定期))
取消(假);
else if(!定期)
//如果不是周期性的,执行调用父类的run方法,也就是构造函数中传递的Runnable对象的run方法。
scheduledfuturetask . super . run();
//任务首先在if的括号中执行
else if(scheduledfuturetask . super . runandreset()){
//如果是周期性的,就需要设置下一次执行时间,然后用reExecutePeriodic方法把任务再次扔进任务队列。
//这里特别需要注意的是,if中的逻辑执行失败。如果没有捕获到异常,那么下面的逻辑将不会再次执行。也就是说,一旦中间执行失败,后面的周期性任务就会失败。
setNextRunTime();
re execute periodic(outer task);
}
}
摘要
ScheduledThreadPoolExecutor通过time参数设置当前任务执行的等待时间,然后通过period设置下一次任务执行的等待时间。这两个参数不是在线程池中设置,而是在任务中携带,可以将线程池与任务完全解耦。
注意:
(1)任务的执行等待时间在队列的take方法中。
(2)当2)period参数设置为0时,任务将只执行一次,而不是多次。
(3)如果要自己实现周期性任务,一定要注意在周期性任务执行过程中捕捉异常,否则一次执行失败会导致后续任务周期失败,任务无法继续执行。
关于java定时器线程池(ScheduledThreadPoolExecutor)的实现,本文到此结束。有关java定时器线程池的更多信息,请搜索我们以前的文章或继续浏览下面的相关文章。希望大家以后能多多支持我们!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。