Timer
类的这两个方法有什么区别:
schedule(TimerTask task, long delay, long period)
和
scheduleAtFixedRate(TimerTask task, long delay, long period)
Documentation并没有清楚地说明它们之间的区别。
发布于 2014-03-19 01:48:46
文档确实解释了其中的区别:
日程安排:
在固定延迟执行中,每次执行都是相对于上次执行的实际执行时间进行调度的。如果由于任何原因(例如垃圾收集或其他后台活动)而延迟执行,则后续执行也将延迟。
因此,假设延迟为5秒,每个任务需要2秒,您将得到
TTWWWTTWWWTTWWWTT
其中T
表示任务执行1秒,W
表示等待1秒。
但现在假设发生了一个长GC (由G
表示),并延迟了第二个任务,第三个任务将在第二个任务开始5秒后启动,就好像长GC没有发生一样:
TTWWWGGTTWWWTTWWWTT
第三个任务在第二个任务开始5秒后开始。
scheduleAtFixedRate:
在固定速率执行中,每次执行都是相对于初始执行的调度执行时间进行调度的。如果由于任何原因(例如垃圾收集或其他后台活动)而延迟执行,则会快速连续发生两次或更多次执行,以“赶上”。
因此,使用与上面相同的延迟和相同的GC,您将得到
TTWWWGGTTWTTWWWTT
第三个任务开始3秒,而不是第二个任务后5秒,以赶上。
发布于 2017-07-17 01:46:29
感谢@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
超时,如果当前任务持续时间超过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);
}
如您所见,在schedule
方法中,period
被转换为负值。让我们来看看在调度时有什么不同。
下面的代码在TimerThread
的mainloop
中。
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
方法,下一个任务的执行时间是基于在this任务运行之前计算的currentTime
。这意味着,每个任务的执行时间只与前一个任务的开始时间相关。
https://stackoverflow.com/questions/22486997
复制相似问题