使用ScheduledExecutorService代替下Timer
1.scheduledExecutorService类方法scheduleAtFixedRate,ScheduleWithFixedDelay区别比较
1.1.ScheduleAtFixedRate 两次任务之间的间隔时间,取决于每次任务执行的时间长短;
假如每个任务执行花费time时间,如果time>= period,则这次任务执行结束后 立刻执行下一次任务。
如果time<period , 则这次任务执行结束后 ,隔 period-time后执行下一次任务。
1.2.ScheduleWithFixedDelay 不受任务执行时间长短影响,固定这次任务执行结束后隔x秒执行下一次.
不管任务花费多少时间,当这次任务执行结束,一定要等delay之后,再执行下一次任务。
package com.example.core.mydemo.timer; import java.util.Date; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * Timer不支持并发。 * * Timer不支持多线程。全部挂在Timer下的任务都是单线程的,任务仅仅能串行运行。假设当中一个任务运行时间过长。会影响到其它任务的运行,然后就可能会有各种接踵而来的问题。如果重开一个Timer?难道要为全部的耗时的Task都单开一个Timer。显然是不太可能。这样就太乱了。 * Timer的线程不捕获异常。TimerTask假设抛出异常,那么Timer唯一的进程就会挂掉,这样挂在Timer下的全部任务都会无法继续运行 * * 为了弥补Timer的缺陷,jdk1.5中引入了并发包。这里面提供的ScheduledExecutorService。详细实现类是:ScheduledThreadPoolExecutor。ScheduledThreadPoolExecutor支持多线程。同一时候在线程中对异常进行了捕获。所以是Timer的完美替换者。 * * 多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。 * */ public class TimerTest { private static ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(2); public static void main(String[] args) { scheduledExecutorService.submit(new Runnable() { @Override public void run() { System.out.println("scheduledExecutorService>>submit"); } }); scheduledExecutorService.execute(new Runnable() { @Override public void run() { System.out.println("scheduledExecutorService>>execute"); } }); //ScheduleAtFixedRate 两次任务之间的间隔时间,取决于每次任务执行的时间长短; /** * 假如每个任务执行花费time时间,如果time>= period,则这次任务执行结束后 立刻执行下一次任务。 * 如果time<period , 则这次任务执行结束后 ,隔 period-time后执行下一次任务。 * * * case1:Thread.sleep(3000); * -----------------scheduledExecutorService>>scheduleAtFixedRate------------------ * pool-1-thread-1begin == 1671768245503 * pool-1-thread-1end == 1671768248503 后4位,延时3秒 * ------------------scheduledExecutorService>>scheduleAtFixedRate------------------ * pool-1-thread-1begin == 1671768249503 后4位,等待4-3=1秒执行 * pool-1-thread-1end == 1671768252504 * *case2:Thread.sleep(5000); * ------------------scheduledExecutorService>>scheduleAtFixedRate------------------ * pool-1-thread-1begin == 1671768390045 * pool-1-thread-1end == 1671768395045 后4位,延时5秒 * ------------------scheduledExecutorService>>scheduleAtFixedRate------------------ * pool-1-thread-1begin == 1671768395045 后4位,立即执行 * pool-1-thread-1end == 1671768400045 */ scheduledExecutorService.scheduleAtFixedRate(new Runnable() { @Override public void run() { System.out.println("------------------scheduledExecutorService>>scheduleAtFixedRate------------------"); System.out.println(Thread.currentThread().getName() + "begin == " + System.currentTimeMillis()); try { Thread.sleep(5000); //任务花费大约3秒 >> 5000 立即执行 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "end == " + System.currentTimeMillis()); } },1,4, TimeUnit.SECONDS); //ScheduleWithFixedDelay 不受任务执行时间长短影响,固定这次任务执行结束后隔x秒执行下一次. //不管任务花费多少时间,当这次任务执行结束,一定要等delay之后,再执行下一次任务。 /** * case1:Thread.sleep(3000); * ------------------scheduledExecutorService>>scheduleWithFixedDelay------------------ * pool-1-thread-1begin == 1671768527054 * pool-1-thread-1end == 1671768530055 后4位,延时3秒 * ------------------scheduledExecutorService>>scheduleWithFixedDelay------------------ * pool-1-thread-1begin == 1671768534056 后4位,固定等待4秒执行 * pool-1-thread-1end == 1671768537056 * *case2:Thread.sleep(5000); * ------------------scheduledExecutorService>>scheduleWithFixedDelay------------------ * pool-1-thread-1begin == 1671768606672 * pool-1-thread-1end == 1671768611672 后4位,延时5秒 * ------------------scheduledExecutorService>>scheduleWithFixedDelay------------------ * pool-1-thread-1begin == 1671768615674 后4位,固定等待4秒执行 * pool-1-thread-1end == 1671768620674 */ scheduledExecutorService.scheduleWithFixedDelay(new Runnable() { @Override public void run() { System.out.println("------------------scheduledExecutorService>>scheduleWithFixedDelay------------------"); System.out.println(Thread.currentThread().getName() + "begin == " + System.currentTimeMillis()); try{ Thread.sleep(5000); //每个任务花费约3秒 >> 5000 仍然需要等待4秒执行 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "end == " + System.currentTimeMillis()); } },1,4, TimeUnit.SECONDS); TimerTask timerTask = new TimerTask(){ public void run(){ System.out.println("scheduledExecutorService>>schedule"); this.cancel();}}; //单次延时执行调度 scheduledExecutorService.schedule(timerTask,1,TimeUnit.SECONDS); //实例化Timer类 Timer timer=new Timer(); timer.schedule(new TimerTask(){ public void run(){ System.out.println("timer>>schedule"); this.cancel();}},1000); } }
2.ScheduledExecutorService与Timer的对比
Timer不支持并发。
Timer不支持多线程。全部挂在Timer下的任务都是单线程的,任务仅仅能串行运行。假设当中一个任务运行时间过长。会影响到其它任务的运行,然后就可能会有各种接踵而来的问题。如果重开一个Timer?难道要为全部的耗时的Task都单开一个Timer。显然是不太可能。这样就太乱了。
Timer的线程不捕获异常。TimerTask假设抛出异常,那么Timer唯一的进程就会挂掉,这样挂在Timer下的全部任务都会无法继续运行
为了弥补Timer的缺陷,jdk1.5中引入了并发包。这里面提供的ScheduledExecutorService。详细实现类是:ScheduledThreadPoolExecutor。ScheduledThreadPoolExecutor支持多线程。同一时候在线程中对异常进行了捕获。所以是Timer的完美替换者。
多线程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。
为什么需要代替Timer:
case1、Timer管理延时任务的缺陷
TimerThread是Thread的子类,可以看出内部只有一个线程
符合我们的预期结果。因为ScheduledThreadPool内部是个线程池,所以可以支持多个任务并发执行
case2、Timer当任务抛出异常时的缺陷
如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行:
ScheduledExecutorService可以保证,task1出现异常时,不影响task2的运行
case3、Timer执行周期任务时依赖系统时间
Timer执行周期任务时依赖系统时间,如果当前系统时间发生变化会出现一些执行上的变化,ScheduledExecutorService基于时间的延迟,不会由于系统时间的改变发生执行变化。
package com.example.core.mydemo.timer; import java.util.Timer; import java.util.TimerTask; /** * TimerThread是Thread的子类,可以看出内部只有一个线程 * 定义了两个任务,预计是第一个任务1s后执行,第二个任务3s后执行,但是看运行结果: * task1 invoked ! 1000 * task2 invoked ! 4000 */ public class TimerTest2 { private static long start; public static void main(String[] args) throws Exception { TimerTask task1 = new TimerTask() { @Override public void run() { System.out.println("task1 invoked ! " + (System.currentTimeMillis() - start)); //case1 // try // { // Thread.sleep(3000); // } catch (InterruptedException e) // { // e.printStackTrace(); // } //case2 //如果TimerTask抛出RuntimeException,Timer会停止所有任务的运行: throw new RuntimeException(); } }; TimerTask task2 = new TimerTask() { @Override public void run() { System.out.println("task2 invoked ! " + (System.currentTimeMillis() - start)); } }; Timer timer = new Timer(); start = System.currentTimeMillis(); timer.schedule(task1, 1000); timer.schedule(task2, 3000); } }
package com.example.core.mydemo.timer; import java.util.TimerTask; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; /** * * 符合我们的预期结果。因为ScheduledThreadPool内部是个线程池,所以可以支持多个任务并发执行 * task1 invoked ! 1003 * task2 invoked ! 3003 */ public class ScheduledThreadPoolExecutorTest { private static long start; public static void main(String[] args) { /** * 使用工厂方法初始化一个ScheduledThreadPool */ ScheduledExecutorService newScheduledThreadPool = Executors.newScheduledThreadPool(2); TimerTask task1 = new TimerTask() { @Override public void run() { System.out.println("task1 invoked ! " + (System.currentTimeMillis() - start)); //case1 // try // { // Thread.sleep(3000); // } catch (Exception e) // { // e.printStackTrace(); // } //case2 //ScheduledExecutorService可以保证,task1出现异常时,不影响task2的运行 throw new RuntimeException(); /** * case3 * Timer执行周期任务时依赖系统时间 * Timer执行周期任务时依赖系统时间,如果当前系统时间发生变化会出现一些执行上的变化,ScheduledExecutorService基于时间的延迟,不会由于系统时间的改变发生执行变化。 */ } }; TimerTask task2 = new TimerTask() { @Override public void run() { System.out.println("task2 invoked ! " + (System.currentTimeMillis() - start)); } }; start = System.currentTimeMillis(); newScheduledThreadPool.schedule(task1, 1000, TimeUnit.MILLISECONDS); newScheduledThreadPool.schedule(task2, 3000, TimeUnit.MILLISECONDS); } }