目录
3. 使用JDK自带的ScheduledExecutorService
schedule和scheduleAtFixedRate的区别
一、定时任务
概念
定时任务是一种自动化执行特定操作的方式,可以根据预定的时间、日期或间隔周期性地执行某些任务。
在平常的生活中,大家肯定是有设置闹钟的习惯,我们需要通过闹钟来提醒我们到这个时刻,我们应该做指定的事情。同样的在编程当中,我们很多时候也是需要实现这样的操作的,到达指定的时刻,我们想要我们的程序去执行某一个事情,比如:指定时间发送邮箱、指定时间发送生日祝福……
以上的种种到达指定时间做指定事情,就是定时任务。
作用
- 自动化任务执行:定时任务能够在预定的时间触发执行某些任务,无需人工干预。这对于需要定期执行的重复性任务非常有效,例如数据备份、统计报表生成、系统维护等。
- 提高效率和准确性:通过定时任务,可以在特定的时间段内自动执行任务,避免了人工操作的疏忽和错误。这样可以提高任务的执行效率和准确性,并降低因人为原因导致的错误风险。
- 节省时间和资源:定时任务可以代替人工手动执行的操作,节省了大量人力资源和时间成本。同时,它也可以合理分配系统资源,避免任务集中导致的系统负载过高。
- 异步执行:定时任务可以在后台异步执行,不会阻塞用户的其他操作。这对于需要执行耗时较长的任务或需要长时间运行的操作非常有用,可以提高系统的响应速度和用户体验。
二、简单定时任务实现方式
今天我们来讨论一下在Java中如何实现定时任务。定时任务在很多场景下都非常有用,例如定期执行清理工作、数据备份、发送通知等。
在Java中,常见的可以实现定时任务的方式有如下几种:
(1)线程类实现定时任务:比如Thread、Runnable、Callable等线程类都可以实现定时任务。
(2)Timer/TimerTask:Java提供了java.util.Timer和java.util.TimerTask类,可以用于创建定时任务。通过创建一个Timer对象,并调用其schedule()方法,可以指定任务的执行时间和执行间隔。然后,创建一个继承自TimerTask的子类,实现具体的任务逻辑,并在run()方法中定义需要执行的代码。最后,将该任务对象通过Timer的schedule()方法进行调度即可。
(3)ScheduledExecutorService:Java提供了java.util.concurrent.ScheduledExecutorService接口,可以用于创建定时任务。通过调用ScheduledExecutorService的scheduleAtFixedRate()或scheduleWithFixedDelay()方法,可以指定任务的执行时间和执行间隔。然后,创建一个实现了Runnable接口的类,实现具体的任务逻辑,并在run()方法中定义需要执行的代码。最后,将该任务对象提交给ScheduledExecutorService进行调度即可。
(4)@Scheduled注解:这个是Spring框架所提供的,通过在方法上添加@Scheduled注解,并设置相应的时间表达式,就可以让方法按照指定的时间间隔自动执行。
1. Thread线程等待(最原始最简单方式)
创建一个thread
,然后让它在while
循环里一直运行着,通过sleep
方法来达到定时任务的效果。
/**
* 匿名内部类实现 java.lang.Runnable 接口
*/
public class ThreadTask {
public static void main(String[] args) {
final long timeInterval = 1000;
//创建线程(匿名内部类方式)
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true){
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
String dateStr = sdf.format(new Date());
System.out.println("线程等待实现定时任务:" + dateStr);
try {
Thread.sleep(timeInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
//开启线程
thread.start();
}
}
public class ThreadTask1 {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
//创建线程(自定义类MyRunnable实现java.lang.Runnable接口)
Thread t = new Thread(runnable);
//开启线程
t.start();
}
}
/**
* 自定义类MyRunnable实现java.lang.Runnable接口
*/
class MyRunnable implements Runnable{
final long timeInterval = 1000;
@Override
public void run() {
while (true){
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
String dateStr = sdf.format(new Date());
System.out.println("线程等待实现定时任务1:" + dateStr);
try {
Thread.sleep(timeInterval);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2. 使用java.util.Timer
JDK
自带的Timer API
算是最古老的定时任务实现方式了。Timer
是一种定时器工具,使用java.util.Timer
工具类。用来在一个后台线程计划执行指定任务。它可以安排任务“执行一次”或者定期“执行多次”。
Timer类核心方法如下:
// 在指定延迟时间后执行指定的任务
schedule(TimerTask task,long delay);
// 在指定时间执行指定的任务。(只执行一次)
schedule(TimerTask task, Date time);
// 延迟指定时间(delay)之后,开始以指定的间隔(period)重复执行指定的任务
schedule(TimerTask task,long delay,long period);
// 在指定的时间开始按照指定的间隔(period)重复执行指定的任务
schedule(TimerTask task, Date firstTime , long period);
// 在指定的时间开始进行重复的固定速率执行任务
scheduleAtFixedRate(TimerTask task,Date firstTime,long period);
// 在指定的延迟后开始进行重复的固定速率执行任务
scheduleAtFixedRate(TimerTask task,long delay,long period);
// 终止此计时器,丢弃所有当前已安排的任务。
cancal();
// 从此计时器的任务队列中移除所有已取消的任务。
purge();
import java.util.Timer;
import java.util.TimerTask;
public class TimerExample {
public static void main(String[] args) {
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("Task executed at: " + System.currentTimeMillis());
}
};
Timer timer = new Timer();
// 安排任务在1秒后执行,并且每隔1秒执行一次
timer.scheduleAtFixedRate(task, 1000, 1000);
}
}
在这个示例中,我们创建了一个Timer
对象,并用scheduleAtFixedRate
方法安排一个TimerTask
在1秒后开始执行,并且每隔1秒执行一次。
Timer 优缺点分析
优点:JDK自带的,简单易用。
缺点:
(1)对系统时间敏感
Timer类的任务调度是基于绝对时间的,而不是相对时间,所以它对系统时间的改变非常敏感。当系统时间发生变化时,可能导致任务执行时间的误差。
(2)不适合高并发场景
由于Timer类使用单个线程执行所有任务,不适合在高并发环境下使用。当任务过多或任务执行时间较长时,会影响整体性能和响应性。
(3)任务的无法持久化
当应用程序关闭或重启时,Timer
中已经调度的任务会丢失。
(4)单线程执行
Timer类内部使用单个线程来执行所有的定时任务。如果某个任务执行时间过长,会影响其他任务的执行,可能导致任务被延迟。
当一个任务的执行时间过长时,会影响其他任务的调度。
import java.text.