一、定时任务
在 java.util
包下提供了 Timer
与 TimerTask
用于提交定时任务,其中 Timer
用于管理定时任务,TimerTask
创建定义定时任务的具体执行内容。
1. 定时任务
TimerTask
使用类似于线程,新建任务类 CustomTask
继承 TimerTask
类并重写 run()
方法,在 run()
方法中定义任务的具体执行逻辑。
如下示例中我定义了一个定时任务打印当前系统时间并自增计数器 num
。
class CustomTask extends TimerTask {
private String taskName;
public CustomTask(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
try {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
String nowadays = formatter.format(LocalDateTime.now());
System.out.printf("Schedule [%s] active, current time: %s%n", this.taskName, nowadays);
} catch (Exception ex) {
System.out.println("Error running thread " + ex.getMessage());
}
num.getAndIncrement();
}
}
2. 任务提交
完成定时任务的创建之后即可通过 Timer
类的 schedule()
方法进行提交,Timer 中维护了一个默认大小为 128 的工作队列用于接收传入定时任务。
schedule(task, delay, period)
方法的三个入参描述参考下表。
方法 | 描述 |
---|---|
task | 需要指定的定时任务。 |
delay | long 类型,任务首次触发时间与当前的时间差。 |
period | long 类型,定时任务间隔周期。 |
如下示例中通过 Timer
提交了一个定时任务,并设置在 2
秒后触发,任务每隔 5
秒执行一次。
public class ScheduleTest {
private volatile AtomicInteger num = new AtomicInteger(0);
@Test
public void timeTaskDemo() throws InterruptedException {
Timer time = new Timer();
long delay = TimeUnit.SECONDS.toMillis(2);
long period = TimeUnit.SECONDS.toMillis(5);
time.schedule(new CustomTask(), delay, period);
while (true) {
if (num.get() > 3) {
time.cancel();
System.out.println("Cancel time task.");
break;
}
}
// purge(): Remove the cancelled task from time queue.
// If timer queue is empty that is eligible for gc
time.purge();
}
}
除了 schedule()
方法 Timer
类中还提供其它相应的操作,具体信息参考下表:
方法 | 描述 |
---|---|
cancel() | 清空排队中定时任务并不再接收新提交任务,但是正在执行的任务不会中断。 |
purge() | 清空队列中已经被 cancel 的任务,当队列为空时即可被 GC。 |
二、定时线程池
1. 基本介绍
上述提到 Timer 类中维护了一个工作队列并以单线程执行定时任务,而 ScheduledExecutorService
可用于创建定时线程任务资源池。
ScheduledExecutorService
提交任务不再限制必须继承于 TimerTask
,任务类继承于线程同样允许。
2. 任务提交
通过 newScheduledThreadPool()
创建定时任务线程池资源,通过构造器指定线程数。
方法 | 作用 |
---|---|
schedule() | 提交定时任务,仅执行一次。 |
scheduleAtFixedRate() | 提交定时任务,任务触发间隔周期是按上次任务开始时间计算。 |
scheduleWithFixedDelay() | 提交定时任务,任务触发间隔周期是按上次任务完成时间计算。 |
ScheduledExecutorService
线程池存在 scheduleAtFixedRate()
与 scheduleWithFixedDelay()
两种方式提交任务,二者的区别如下:
scheduleAtFixedRate()
间隔时间是从上一个任务开始时计算,无论上个任务是否已经结束。scheduleWithFixedDelay()
间隔时间是从上个任务完成时开始计算,只有当上个任务结束才会开始计时。
public void scheduledPoolDemo() throws Exception {
ScheduledExecutorService executor = Executors.newScheduledThreadPool(3);
int start = 1;
int interval = 3;
// 当前时间 1 秒后执行一次
executor.schedule(() -> {
System.out.println("Task-1 running.");
}, interval, TimeUnit.SECONDS);
// 当前时间 1 秒后执行, 且每隔 3 秒重复执行
// (间隔时间:上一次任务开始时计时)
executor.scheduleAtFixedRate(new Task("fixed-rate"), start, interval, TimeUnit.SECONDS);
// 当前时间 1 秒后执行, 上一任务执行完成 3 秒后重复执行
// (间隔时间:上一次任务结束时计时)
executor.scheduleWithFixedDelay(new Task("fixed-delay"), start, interval, TimeUnit.SECONDS);
executor.shutdown();
}