在Java中,Timer类主要用于定时性、周期性任务 的触发,这个类中有两个方法比较难理解,那就是schedule和scheduleAtFixedRate方法,在这里就用实例分析一下
(1)在方法不延迟的情况下,即间隔时间大于任务执行时间时:
package test; import java.util.Calendar; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class Test1 { static class MyTask extends TimerTask{ public void run(){ try { System.out.println("begin timer="+System.currentTimeMillis()); Thread.sleep(1000); System.out.println(" end timer="+System.currentTimeMillis()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public static void main(String[] args) { MyTask task = new MyTask(); Calendar caldef = Calendar.getInstance(); Date runDate = caldef.getTime(); Timer timer = new Timer(); timer.schedule(task, runDate,3000); } }
schedule方法输出 结果:
begin timer=1490009857383
end timer=1490009858383
begin timer=1490009860383
end timer=1490009861383
begin timer=1490009863383
end timer=1490009864383
scheduleAtFixedRate方法输出结果:
begin timer=1490009967115
end timer=1490009968116
begin timer=1490009970096
end timer=1490009971096
begin timer=1490009973096
end timer=1490009974096
可以看出因为间隔3秒大于任务执行时间1秒,2个方法都是以开始时间每隔3秒固定执行一次,下一次开始时间都是以上一次开始时间加上固定间隔执行
(2)当延迟的情况下,即任务执行时间大于间隔时
static class MyTask extends TimerTask{
public void run(){
try {
System.out.println("begin timer="+System.currentTimeMillis());
Thread.sleep(5000);
System.out.println(" end timer="+System.currentTimeMillis());
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyTask task = new MyTask();
Calendar caldef = Calendar.getInstance();
Date runDate = caldef.getTime();
Timer timer = new Timer();
timer.schedule(task, runDate,3000);
}
}
schedule方法输出:
begin timer=1490010207645
end timer=1490010212646
begin timer=1490010212646
end timer=1490010217646
begin timer=1490010217646
end timer=1490010222646
scheduleAtFixedRate方法输出结果:
begin timer=1490010370413
end timer=1490010375413
begin timer=1490010375413
end timer=1490010380413
begin timer=1490010380413
end timer=1490010385414
可以看出因为任务时间5秒大于时间间隔3秒时,2个方法都是每隔5秒固定执行一次,下一次开始时间都是以上一次结束时间为准
(3)验证2种方法的追赶性,即任务计划执行时间早于任务执行时间
public void run(){
System.out.println("begin timer="+new Date());
System.out.println(" end timer="+new Date());
}
}
public static void main(String[] args) {
MyTask task = new MyTask();
System.out.println("现在执行时间: "+new Date());
Calendar caldef = Calendar.getInstance();
caldef.set(Calendar.SECOND, caldef.get(Calendar.SECOND)-20);
Date runDate = caldef.getTime();
System.out.println("计划执行时间: "+runDate);
Timer timer = new Timer();
timer.schedule(task, runDate,2000);
}
schedule方法输出:
现在执行时间: Mon Mar 20 19:53:56 CST 2017
计划执行时间: Mon Mar 20 19:53:36 CST 2017
begin timer=Mon Mar 20 19:53:56 CST 2017
end timer=Mon Mar 20 19:53:56 CST 2017
begin timer=Mon Mar 20 19:53:58 CST 2017
end timer=Mon Mar 20 19:53:58 CST 2017
begin timer=Mon Mar 20 19:54:00 CST 2017
end timer=Mon Mar 20 19:54:00 CST 2017
scheduleAtFixedRate方法输出结果:
现在执行时间: Mon Mar 20 19:55:02 CST 2017
计划执行时间: Mon Mar 20 19:54:42 CST 2017
begin timer=Mon Mar 20 19:55:02 CST 2017
end timer=Mon Mar 20 19:55:02 CST 2017
begin timer=Mon Mar 20 19:55:02 CST 2017
end timer=Mon Mar 20 19:55:02 CST 2017
begin timer=Mon Mar 20 19:55:02 CST 2017
end timer=Mon Mar 20 19:55:02 CST 2017
begin timer=Mon Mar 20 19:55:02 CST 2017
end timer=Mon Mar 20 19:55:02 CST 2017
begin timer=Mon Mar 20 19:55:02 CST 2017
end timer=Mon Mar 20 19:55:02 CST 2017
begin timer=Mon Mar 20 19:55:02 CST 2017
end timer=Mon Mar 20 19:55:02 CST 2017
begin timer=Mon Mar 20 19:55:02 CST 2017
end timer=Mon Mar 20 19:55:02 CST 2017
begin timer=Mon Mar 20 19:55:02 CST 2017
end timer=Mon Mar 20 19:55:02 CST 2017
begin timer=Mon Mar 20 19:55:02 CST 2017
end timer=Mon Mar 20 19:55:02 CST 2017
begin timer=Mon Mar 20 19:55:02 CST 2017
end timer=Mon Mar 20 19:55:02 CST 2017
begin timer=Mon Mar 20 19:55:02 CST 2017
end timer=Mon Mar 20 19:55:02 CST 2017
begin timer=Mon Mar 20 19:55:04 CST 2017
end timer=Mon Mar 20 19:55:04 CST 2017
begin timer=Mon Mar 20 19:55:06 CST 2017
end timer=Mon Mar 20 19:55:06 CST 2017
begin timer=Mon Mar 20 19:55:08 CST 2017
end timer=Mon Mar 20 19:55:08 CST 2017
相信你已经看得很明白了scheduleAtFixedRate方法具有追赶性,他会把没执行的计划任务,在任务开始时,追赶重新执行完,而 schedule则不具备追赶性,从当前时间开始往后间隔执行任务
总结:
Timer不支持多线程,所有挂在Timer下的任务都是单线程的,任务只能串行执行,如果其中一个任务执行时间过长,会影响到其他任务的执行,然后就可能会有各种接踵而来的问题。
Timer的线程不捕获异常,TimerTask如果抛出异常,那么Timer唯一的进程就会挂掉,这样挂在Timer下的所有任务都会无法继续执行。
经过以上分析,也就明白为什么jdk1.5中引入了并发包,这里面提供的ScheduledExecutorService。具体实现类是:ScheduledThreadPoolExecutor。ScheduledThreadPoolExecutor支持多线程,同时在线程中对异常进行了捕获。所以是Timer的完美替换者。
下面是一个简单实例
@SuppressWarnings("deprecation")
@Override
public void run() {
System.out.println("----task1 start--------"+new Date().toLocaleString());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("----3s later, task1 end--------"+new Date().toLocaleString());
}
}
public class Task2 extends TimerTask{ @SuppressWarnings("deprecation") @Override public void run() { System.out.println("----task2 start--------"+new Date().toLocaleString()); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("----5s later, task2 end--------"+new Date().toLocaleString()); } }
public static void main(String[] args) { ScheduledExecutorService pool = Executors.newScheduledThreadPool(2);//启用2个线程 Task1 t1 = new Task1(); // 立即执行,任务消耗3秒,执行结束后等待2秒,【有空余线程时】,再次执行该任务 pool.scheduleWithFixedDelay(t1, 0, 2, TimeUnit.SECONDS); // 立即执行,任务消耗5秒,执行结束后等待2秒,【有空余线程时】,再次执行该任务 Task2 t2 = new Task2(); pool.scheduleWithFixedDelay(t2, 0, 2, TimeUnit.SECONDS); }