使用ScheduledExecutorService代替下Timer
1.scheduledExecutorService类方法scheduleAtFixedRate,ScheduleWithFixedDelay区别比较
1.1.ScheduleAtFixedRate 两次任务之间的间隔时间,取决于每次任务执行的时间长短; 假如每个任务执行花费time时间,如果time>= period,则这次任务执行结束后 立刻执行下一次任务。 如果time<period , 则这次任务执行结束后 ,隔 period-time后执行下一次任务。
1.2.ScheduleWithFixedDelay 不受任务执行时间长短影响,固定这次任务执行结束后隔x秒执行下一次. 不管任务花费多少时间,当这次任务执行结束,一定要等delay之后,再执行下一次任务。
代码语言:javascript复制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基于时间的延迟,不会由于系统时间的改变发生执行变化。
代码语言:javascript复制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);
}
}
代码语言:javascript复制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);
}
}