一、定时器概述
二、定时器的实现方式
- 使用sleep() 方法,设置睡眠时间,在某个时间点唤醒相关执行(相当于原始定时器,不怎么用)
- Java中的类库:java.util.Timer,用于Java线程里指定时间或周期运行任务,Timer是线程安全的,但不提供实时性(real-time)保证,使用也不多,因为现在高级框架都是支持定时任务的。TimerTask把我们的业务逻辑写好之后,使用Timer定时执行即可
- 使用框架,目前使用较多的是Spring中提供的SpringTask框架,通过简单的配置就可以完成定时任务,但底层也是基于Timer来实现的
三、Timer
- Timer 可以按计划执行重复的任务或者定时执行指定任务,这是因为 Timer 内部利用了一个后台线程 TimerThread 有计划地执行指定任务,即对于每一个 Timer 对象而言,其内部都是对应着单个后台线程,这个线程用于顺序执行优先队列中所有的计时器任务
- Timer是一个实用工具类,该类用来调度一个线程,使它可以在将来某一时刻执行,Java 的 Timer 类可以调度一个任务运行一次或定期循环运行,另外定时器中的操作要尽可能花费短的时间
- Timer 劣势:
Timer 是基于绝对时间的,对系统时间比较敏感
Timer 内部是单一线程,不支持多个任务并发执行
Timer 运行多个 TimeTask 时,只要其中之一抛出异常,其它任务便会自动终止运行
Timer 不明确任务实际执行策略,不方便自行控制
Timer 存在潜在内存泄漏问题,因为默认 Timer 执行线程不是 daemon 线程, 任务执行完,主线程(或其他启动定时器的线程)结束时,task 线程并没有结束
1. 构造方法
1.1 无参构造方法
public Timer() {
this("Timer-" + serialNumber());
}
1.2 有参构造方法
- 创建一个可以指定相关线程作为守护线程运行的定时器对象
public Timer(boolean isDaemon) {
this("Timer-" + serialNumber(), isDaemon);
}
public Timer(String name) {
thread.setName(name);
thread.start();
}
- 创建一个指定名称,相关线程作为守护线程运行的定时器对象
public Timer(String name, boolean isDaemon) {
thread.setName(name);
thread.setDaemon(isDaemon);
thread.start();
}
四、TimerTask
- TimerTask是一个抽象类,实现了 Runnable 接口,代表一个可被执行的任务,我们需要扩展该类以便创建自己的TimerTask,这个TimerTask可以被Timer调度
- Timer计划的任务被TimerTask封装,通过继承TimerTask类并实现 run() 方法来自定义要执行的任务,即TimerTask把我们得业务逻辑写好之后,使用Timer定时执行
- 步骤:
创建一个Timer
创建一个TimerTask
使用Timer执行TimerTask
五、schdule
- 安排在指定延迟后执行指定的任务(以当前时间为基准,延迟指定的毫秒后执行一次task任务)
public void schedule(TimerTask task, long delay) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
sched(task, System.currentTimeMillis()+delay, 0);
}
- 安排在指定的时间执行指定的任务(在指定的日期执行一次task任务,如果日期time早于当前时间,则立刻执行)
public void schedule(TimerTask task, Date time) {
sched(task, time.getTime(), 0);
}
- 安排指定的任务从指定的延迟后开始进行重复固定延迟执行TimerTask任务(指定任务task在指定延迟delay后进行固定频率peroid的执行)
public void schedule(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, -period);
}
- 安排指定的任务在指定的时间开始进行重复的固定延迟执行(指定的任务task在指定的时间firstTime开始进行重复的固定速率period执行)
public void schedule(TimerTask task, Date firstTime, long period) {
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, firstTime.getTime(), -period);
}
六、scheduleAtFixedRate
- 安排指定的任务在指定的延迟后开始进行重复的固定速率执行(以当前时间为基准,延迟指定的毫秒后,再按指定的时间间隔周期性地无限次数的执行TimerTask任务,如果日期firstTime早于当前时间,则立刻执行,且不执行在时间差内的任务)
public void scheduleAtFixedRate(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, period);
}
- 安排指定的任务在指定的时间开始进行重复的固定速率执行(在指定的日期之后,按指定的时间间隔周期性地无限次数的执行TimerTask任务,如果日期firstTime早于当前时间,则立即执行,并补充性的执行在时间差内的任务)
public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) {
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, firstTime.getTime(), period);
}
七、测试用例
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class TimerTest {
public static void main(String[] args) {
Timer timer = new Timer();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date firstTime;
try {
firstTime = sdf.parse("2020-09-09 15:55:50");
timer.scheduleAtFixedRate(new MyTimerTask(), firstTime, 1000 * 10);
} catch (ParseException e) {
e.printStackTrace();
}
}
}
class MyTimerTask extends TimerTask {
@Override
public void run() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String strTime = sdf.format(new Date());
System.out.println(strTime + "完成任务");
}
}