延时队列-Timer
Posted a-yes
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了延时队列-Timer相关的知识,希望对你有一定的参考价值。
Timer
1. 使用方法
public class TimerTest {
public static void main(String[] args) {
Timer timer = new Timer();
System.out.format("name:{%s},time:{%s},thread:{%s}
", "start", new Date(), Thread.currentThread());
timer.schedule(new TimerTask() {
@SneakyThrows
@Override
public void run() {
System.out.format("name:{%s},time:{%s},thread:{%s}
", "5s", new Date(), Thread.currentThread());
Thread.sleep(5000);
}
}, 1000);
timer.scheduleAtFixedRate(new TimerTask() {
@SneakyThrows
@Override
public void run() {
System.out.format("name:{%s},time:{%s},thread:{%s}
", "5s2", new Date(), Thread.currentThread());
Thread.sleep(5000);
}
}, 2000);
}
}
2. 源码解析
构造方法
//默认线程名
public Timer() {
this("Timer-" + serialNumber());
}
//是否有守护线程
public Timer(boolean isDaemon) {
this("Timer-" + serialNumber(), isDaemon);
}
//自定义线程名称
public Timer(String name) {
thread.setName(name);
thread.start();
}
//自定义名称和设置是否有守护线程
public Timer(String name, boolean isDaemon) {
thread.setName(name);
thread.setDaemon(isDaemon);
thread.start();
}
「构造方法中的thread是Timer的内部类, TimerThread extends Thread」,构造方法启动线程 , TimerThread持有一个小顶堆队列,TaskQueue
TimerThread extends Thread
public void run() {
try {
//主要逻辑放在mainLoop中
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
}
/**
* The main timer loop. (See class comment.)
*/
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// 如果队列为空且执行状态正常则等待
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // Queue is empty and will forever remain; die
// Queue nonempty; look at first evt and do the right thing
long currentTime, executionTime;
//获取到期时间最短的任务
task = queue.getMin();
synchronized(task.lock) {
//如果任务取消,则弹出继续
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue; // No action required, poll queue again
}
//当前时间
currentTime = System.currentTimeMillis();
//任务的下次执行时间
executionTime = task.nextExecutionTime;
//如果执行时间到期了
if (taskFired = (executionTime<=currentTime)) {
//如果不需要重复,则把此任务弹出,并把状态设为已执行
if (task.period == 0) { // Non-repeating, remove
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
//需要重复
// 由设定任务可知,task.period<0的都是 schedule 方法
// 当是schedule方法时,下次执行时间为当期时间+间隔时间
// 当是scheduleAtFixedRate方法时,下次执行时间为 任务中上次计算出的下次执行时间+间隔时间
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // Task hasn‘t yet fired; wait
queue.wait(executionTime - currentTime);
}
//执行任务
if (taskFired) // Task fired; run it, holding no locks
task.run();
} catch(InterruptedException e) {
}
}
}
设定任务
一共有6个方法,
//延迟一定时间后执行一次
public void schedule(TimerTask task, long delay) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
sched(task, System.currentTimeMillis()+delay, 0);
}
//在指定时间执行一次
public void schedule(TimerTask task, Date time) {
sched(task, time.getTime(), 0);
}
//延迟一定时间后执行,并间隔一定时间重复执行
public void schedule(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, -period);
}
//在指定时间执行一次,并间隔一定时间重复执行
public void schedule(TimerTask task, Date firstTime, long period) {
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, firstTime.getTime(), -period);
}
//延迟一定时间后执行,并间隔一定时间重复执行(与)
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, period);
}
//在指定时间执行一次,并间隔一定时间重复执行
public void scheduleAtFixedRate(TimerTask task, Date firstTime,
long period) {
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, firstTime.getTime(), period);
}
?schedule与scheduleAtFixedRate主要区别是,如果执行的任务耗时超过了时间间隔,下次的执行时间,schedule是以当前时间+时间间隔计算下次执行时间,scheduleAtFixedRate是以上次计算出的下次执行时间+时间间隔计算下次执行时间
?
可以看到其实这6个方法都是调用的 sched(TimerTask task, long time, long period)方法,
private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
// Constrain value of period sufficiently to prevent numeric
// overflow while still being effectively infinitely large.
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1;
synchronized(queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
}
//任务加入小顶堆
queue.add(task);
if (queue.getMin() == task)
queue.notify();
}
}
举例
举例说明scheduleAtFixedRate和schedule区别
设置一个定时任务,每个5秒重复一次,第一次执行耗时6秒,之后每次执行耗时3秒.
采用schedule方法,则第二次执行时间为第一次执行结束后+5s , 即第11秒执行第二次
采用scheduleAtFixedRate方法,则第二次执行时间为第一次重复时间+5s,即第10秒执行第二次,
「可以看出schedule方法保证的是前一个任务结束时间和后一个任务开始时间的间隔,scheduleAtFixedRate保证的是任意两个任务开始时间的间隔。」
3. 总结
Timer只有一个单线程在轮训任务队列 Timer使用synchronized关键字保证线程安全 Timer可以在任务里使用线程进行耗时操作,防止耗时太久影响下个任务执行 当有任务抛出RuntimeException异常时,所有任务都会停止,注意执行任务的异常捕获
本文使用 mdnice 排版
以上是关于延时队列-Timer的主要内容,如果未能解决你的问题,请参考以下文章
Flutter延时任务Flutter通过Future与Timer实现延时任务