总的来说,实现定时任务,大致有四种方式,timer,ScheduledThreadPool,spring的@Scheduled注解和Quartz框架。
注解基于spring框架,添加cron表达式内容即可,Quartz是框架,在这里暂时不做赘述。
详细比较timer和ScheduledThreadPool两种方式:
timer只支持单线程,ScheduledThreadPool可以支持多线程。
1、timer执行多个任务调度,有一个任务抛出异常,其他任务停止;ScheduledThreadPool则会继续执行别的任务:
time:
Timer timer = new Timer("Time");
TimerTask task1 = new TimerTask() {
@Override
public void run() {
System.out.println("程序开始于:" + new Date());
throw new RuntimeException("Exception in task1");
}
};
TimerTask task2 = new TimerTask() {
@Override
public void run() {
System.out.println("程序开始于:" + new Date());
}
};
long delay = 0L;
long period = 1000L;
timer.scheduleAtFixedRate(task1,delay,period);
timer.scheduleAtFixedRate(task2,delay + 500,period);
// 主线程等待,以便观察效果
try {
Thread.sleep(5000); // 主线程等待5秒
} catch (InterruptedException e) {
e.printStackTrace();
}
timer.cancel();
输出日志为:
程序开始于:Fri Jul 19 15:02:29 CST 2024
Exception in thread "Time" java.lang.RuntimeException: Exception in task1
at com.wutongservice.user.controller.DemoTest$1.run(DemoTest.java:23)
at java.util.TimerThread.mainLoop(Timer.java:555)
at java.util.TimerThread.run(Timer.java:505)
ScheduledThreadPool:
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable task1 = () -> {
System.out.println("程序1开始于:" + new Date());
throw new RuntimeException("Exception in task1");
};
Runnable task2 = () -> {
System.out.println("程序2开始于:" + new Date());
};
long initialDelay = 0;
long period = 1;
scheduler.scheduleAtFixedRate(task1,initialDelay,period, TimeUnit.MILLISECONDS);
scheduler.scheduleAtFixedRate(task2,initialDelay + 500,period, TimeUnit.MILLISECONDS);
// 主线程等待,以便观察效果
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
scheduler.shutdown();
}
输出日志:
程序1开始于:Fri Jul 19 15:06:44 CST 2024
程序2开始于:Fri Jul 19 15:06:45 CST 2024
程序2开始于:Fri Jul 19 15:06:45 CST 2024
程序2开始于:Fri Jul 19 15:06:45 CST 2024
程序2开始于:Fri Jul 19 15:06:45 CST 2024
程序2开始于:Fri Jul 19 15:06:45 CST 2024
程序2开始于:Fri Jul 19 15:06:45 CST 2024
程序2开始于:Fri Jul 19 15:06:45 CST 2024
程序2开始于:Fri Jul 19 15:06:45 CST 2024
2、timer只支持单线程,一个任务执行完才会执行下一个任务;ScheduledThreadPool支持多线程,各个任务都是并发的执行。
timer:
Timer timer = new Timer("Time");
//模拟一个长时间任务
TimerTask task1 = new TimerTask() {
@Override
public void run() {
System.out.println("task1开始于:" + new Date());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
};
//正常的打印任务
TimerTask task2 = new TimerTask() {
@Override
public void run() {
System.out.println("task2开始于:" + new Date());
}
};
long delay = 0L;
long period = 1000L;
timer.scheduleAtFixedRate(task1,delay,period);
timer.scheduleAtFixedRate(task2,delay + 500,period);
// 主线程等待,以便观察效果
try {
Thread.sleep(10000); // 主线程等待10秒
} catch (InterruptedException e) {
e.printStackTrace();
}
timer.cancel();
输出日志:
task1开始于:Fri Jul 19 15:19:11 CST 2024
task2开始于:Fri Jul 19 15:19:14 CST 2024
task1开始于:Fri Jul 19 15:19:14 CST 2024
task2开始于:Fri Jul 19 15:19:17 CST 2024
task1开始于:Fri Jul 19 15:19:17 CST 2024
task2开始于:Fri Jul 19 15:19:20 CST 2024
task1开始于:Fri Jul 19 15:19:20 CST 2024
任务2在任务1执行完才执行
ScheduledThreadPool:
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
//模拟一个长时间任务
Runnable task1 = () -> {
System.out.println("程序1开始于:" + new Date());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("程序1结束于:" + new Date());
};
//正常的打印任务
Runnable task2 = () -> {
System.out.println("程序2开始于:" + new Date());
};
long initialDelay = 0;
long period = 1;
scheduler.scheduleAtFixedRate(task1,initialDelay,period, TimeUnit.MILLISECONDS);
scheduler.scheduleAtFixedRate(task2,initialDelay + 500,period, TimeUnit.MILLISECONDS);
// 主线程等待,以便观察效果
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
scheduler.shutdown();
输出日志:
程序1开始于:Fri Jul 19 15:28:38 CST 2024
程序2开始于:Fri Jul 19 15:28:39 CST 2024
程序2开始于:Fri Jul 19 15:28:39 CST 2024
程序2开始于:Fri Jul 19 15:28:39 CST 2024
程序2开始于:Fri Jul 19 15:28:39 CST 2024
程序2开始于:Fri Jul 19 15:28:39 CST 2024
程序2开始于:Fri Jul 19 15:28:39 CST 2024
程序2开始于:Fri Jul 19 15:28:39 CST 2024
程序2开始于:Fri Jul 19 15:28:39 CST 2024
程序2的执行不受第一个任务的影响。
总结来说,当执行多个定时任务时,使用ScheduledThreadPool效果远远优于使用timer。