Timer原理解析
jdk版本:1.8
一.使用场景
Timer可以用来触发或执行定时性任务,周期性任务,或者两者同时兼有。
为此Timer提供了 以下API:
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@SneakyThrows
@Override
public void run() {
System.out.println(Thread.currentThread().getId() + "===>" + System.currentTimeMillis());
Thread.sleep(1000);
}
};
Date date = new Date(System.currentTimeMillis() + 1000);
//1.在date时间点执行task任务
timer.schedule(task, date);
//2.在date时间点执行task任务,此后每过1秒执行一次
// 下面的scheduleAtFixedRate实际是调用了schedule
timer.schedule(task, date, 1000);
timer.scheduleAtFixedRate(task,date,1000);
//3.延时触发,2秒后执行task任务
timer.schedule(task,2000);
//4.延时触发,1秒后执行task任务,此后每过2秒执行一次,
// 下面的scheduleAtFixedRate实际是调用了schedule
timer.schedule(task,1000,2000);
timer.scheduleAtFixedRate(task, 1000, 2000);
二.运行原理
Timer的两个重要的属性:
- TaskQueue queue:用于存放TimerTask的一个优先级队列(拥有最小nextExecutionTime的TimerTask会被放到queue[1]);
- TimerThread thread:Timer运行方式的是单线程,这里的单线程指的就是这个thread,thread在一个while (true)循环里,获取并处理queue里面的TimerTask。
Timer的重要步骤:
1.Timer timer = new Timer()
这一步创建了Timer,同时启动了维护Timer运行的线程thread,在thread的run方法里有一个最重要的mainLoop()方法,Timer就是使用该方法一直循环监控并处理queue里面的任务:
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// 判断装TimerTask的queue是否为空,为空则进入等待
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // Queue is empty and will forever remain; die
// Queue nonempty; look at first evt and do the right thing
long currentTime, executionTime;
//queue非空就获取queue[1]的TimerTask,queue[1]拥有最小的nextExecutionTime值
task = queue.getMin();
synchronized(task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue; // No action required, poll queue again
}
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) {
//不许重复执行的任务,从queue拿出来后,就删除,避免重复执行
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else {
//需要重复执行的任务,则重置queue[1]的nextExecutionTime为currentTime + |task.period|
//并根据 nextExecutionTime对queue进行升序排列
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // Task hasn't yet fired; wait
queue.wait(executionTime - currentTime);
}
if (taskFired) // Task fired; run it, holding no locks
//执行TimerTask任务
task.run();
} catch(InterruptedException e) {
}
}
}
2.timer.schedule(task, xxxxxxxxxxx)
这一步是将TimerTask放入queue中,这样thread在mainLoop循环循环中才可以取到数据并处理,源码如下(以schedule(TimerTask task, long delay, long period)为例):
private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
// Constrain value of period sufficiently to prevent numeric
// overflow while still being effectively infinitely large.
if (Math.abs(period) > (Long.MAX_VALUE >> 1))
period >>= 1;
synchronized(queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
//封装TimerTask
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
}
//这里最终要的就是这一步:将TimerTask放入queue中
queue.add(task);
if (queue.getMin() == task;
//唤醒mainLoop方法里wait状态中的queue,让mainloop方法继续执行下去
queue.notify();
}
}