Timer类有两种执行模式:
fixed-rate:对应于 scheduleAtFixedRate 方法
fixed-delay:对应于 schedule 方法
fixed-rate 模式,如果某次任务超时了,它会智能的缩短下一次任务的执行时间,
比如你 10 秒一次任务,但是任务执行了 15 秒,
那么下一次任务如果没有超时的话,可能也不会分配给他完整的 10 秒,而是 5 秒
目的:是尽量让后面的任务可以追赶上来
任务执行时间可能是这样一个模式:
第一次任务:15 秒(超时)
第二次任务:8 秒(未超时)
第三次任务:7 秒(未超时)
第四次任务:10 秒(未超时)
……
fixed-delay 模式,如果你某次任务超时了,也不会影响下一次任务的执行时间
10 秒周期的任务,执行了 15 秒
那下一次任务依然是分配完整的 10 秒
任务执行时间可能是这样一个模式:
第一次任务:15 秒(超时)
第二次任务:10 秒(未超时)
第三次任务:10 秒(未超时)
第四次任务:15 秒(超时)
举个使用的例子:
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Main {
public static void main(String args[]) {
TimerTask task = new TimerTask() {
Calendar cal = Calendar.getInstance();
@Override
public void run() {
Date date = cal.getTime();
// 2016/05/05-01:01:34:364
System.out.println(new SimpleDateFormat("yyyy/MM/dd-HH:mm:ss:SSS").format(date));
//System.out.println();
// TODO Auto-generated method stub
System.out.println("lalala");
long a = (int)(Math.random() * 500);
System.out.println(a);
try {
Thread.sleep(a);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
Timer timer = new Timer();
long delay = 0;
long inte = 1 * 1000;
timer.schedule(task, delay, inte);
}
}
注意:TimerTask类是一个抽象类(包含一个run抽象方法),实现了Runnable接口。所以在实例化前需要重写run方法,将需要执行任务代码放入这个run方法中。
方法schedule和方法scheduleAtFixedRate之间最大的而区别就是:追赶性。
任务调度要优先考虑实时保证
由于Java的天性,并且在开发JDK的过程中要考虑到不同平台,而不同平台的线程调度机制是不同的,因此各种平台下JVM 的线程调度机制也是不一致的。从而Timer不能保证任务在所指定的时间内执行。另外由于TimerTask是实现Runnable接口的,在TimerTask被放进线程队列睡眠一段时间(wait)之后,当到了指定的该唤起该TimerTask时,由于执行的确切时机取决于JVM的调度策略和当前还有多少线程在等待CPU处理。因此就不能保证任务在所指定的时间内执行。通常在如下两种情况下导致任务延迟执行:
(1) 有大量线程在等待执行
(2) GC机制的影响导致延迟
这也是为什么在Timer API中存在两组调度方法的原因。即:
(1) schedule():用固定延迟调度。使用本方法时,在任务执行中的每一个延迟会传播到后续的任务的执行。
(2) scheduleAsFixedRate():用固定比率调度。使用本方法时,所有后续执行根据初始执行的时间进行调度,从而减小延迟。
具体使用哪一个方法取决于哪些参数对你的程序或系统更重要。
两个方法相同点:如果执行任务的时间被延迟时,那么下一次任务的执行时间是以上一次任务“结束”时的时间作为参考来计算的。