schedule和scheduleAtFixedRate有什么区别?

这两种Timer类方法有什么区别:

 schedule(TimerTask task, long delay, long period) 

 scheduleAtFixedRate(TimerTask task, long delay, long period) 

文档没有明确区别。

文档确实解释了区别:

时间表:

在固定延迟执行中,每次执行都是相对于前一次执行的实际执行时间进行调度的。 如果执行因任何原因(例如垃圾收集或其他后台活动)而延迟,则后续执行也将延迟。

所以,假设延迟是5秒,每个任务需要2秒,你会得到

 TTWWWTTWWWTTWWWTT 

其中T表示任务执行1秒, W表示1秒等待。

但是现在假设一个长GC(由G表示)发生并延迟第二个任务,第三个任务将在第二个任务开始后5秒开始,好像长GC不会发生:

 TTWWWGGTTWWWTTWWWTT 

第三个任务在第二个任务开始后5秒开始。

scheduleAtFixedRate:

在固定速率执行中,每次执行都是相对于初始执行的预定执行时间进行调度的。 如果由于任何原因(例如垃圾收集或其他后台活动)延迟执行,则会快速连续执行两次或更多次执行以“赶上”。

因此,与上面相同的延迟和相同的GC,你会得到

 TTWWWGGTTWTTWWWTT 

第三个任务任务开始3秒而不是第二个任务后的5个任务,以赶上。

谢谢@Nizet的回答,我为一些想练习和学习的人写了一个示例代码。

 import java.util.Timer; import java.util.TimerTask; public class TimerTest { public static void main(String args[]){ TimerTest.DelayTask task = new DelayTask(); Timer timer = new Timer(); /** * Use schedule or scheduletAtFixedrate and check the printed result */ timer.schedule(task, 0, 5000); //timer.scheduleAtFixedRate(task, 0, 5000); } public static boolean stop = false; public static void delayOneSec(String status){ try{ System.out.print(status); Thread.sleep(1000); }catch(Exception e){ e.printStackTrace(); } } static class DelayTask extends TimerTask{ int count = 2; @Override public void run() { // TODO Auto-generated method stub stop = true; for(int i = 0; i < count; i++){ TimerTest.delayOneSec("T"); } if(count == 2){ count = 6; }else{ count = 2; } stop = false; new PrintW().start(); } } static class PrintW extends Thread{ @Override public void run(){ while(!stop){ TimerTest.delayOneSec("W"); } } } } 

任务本身将重复花费2秒或6秒。 让我们看看每个场景的结果。

使用timer.schedule(task, 0, 5000); ,输出为TTWWWTTTTTTTTWWWTTTTTTTTWWWTTTTTTTT 。 如您所见,计时器遵循以下规则,如果任务及时完成则等待时间超时,如果当前任务持续超过period则立即启动下一个任务。

使用timer.scheduleAtFixedRate(task, 0, 5000); ,输出为TTWWWTTTTTTTTWWTTTTTTTTWWTTTTTTTTWWTTTTTTTTWWTTTTTTTTWWTTTTTTTT 。 现在情况有点不同了。 javadoc

两次或更多次执行将迅速连续“赶上”。

在这里生效。 如您所见,忽略第一个TTWWW ,每两个任务将打印TTTTTTTTWW并持续10秒(两个周期)。

让我们深入研究Timer的源代码。

 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); } 

如您所见, periodschedule方法中转移为负值。 让我们看看在安排它时有什么不同。

下面的代码在mainloopTimerThread

 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 queue.rescheduleMin( task.period<0 ? currentTime - task.period : executionTime + task.period); } } } 

这是魔术发生的地方,对于schedule方法,下一个任务执行时间基于在此任务运行之前计算的currentTime 。 这意味着,每个任务的执行时间仅与先前任务的开始时间相关。