携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第4天,点击查看活动详情
源码解读
TimerThread执行流程
当我们创建Timer类之后,就会启动一个TimerThread执行线程,线程主要执行了一个mainLoop()方法,大致流程如下:
源码:
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
//如果queue中没有要执行的任务并且还没有添加任务时,则先挂起等待
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
//如果TimerThread被激活,queue里面还是没有任务
//则该线程的无限循环,直到等有新任务出现
if (queue.isEmpty())
break;
long currentTime, executionTime;
//获取queue队列里面下一个要执行的任务(根据时间排序,也就是接下来最近要执行的任务)
task = queue.getMin();
synchronized(task.lock) {
//是否取消任务,通过TimerTask.cancen()可以取消
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue;
}
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
//task.period==0表示这个任务只需要执行一次,这里就从queue里面移除了
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) {
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else {
//执行多次任务,需要重新计算下次执行时间点
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
//如果任务的下次执行时间还没有到达,则挂起TimerThread线程executionTime - currentTime毫秒数
//到达执行时间点再自动激活
if (!taskFired) // Task hasn't yet fired; wait
queue.wait(executionTime - currentTime);
}
// 如果任务的下次执行时间到了,则执行任务
// 注意:这里任务执行没有另起线程,还是在TimerThread线程执行的,所以当有任务在同时执行时会出现阻塞
// 这里没有try catch异常,当TimerTask抛出异常会导致整个TimerThread跳出循环,从而导致Timer失效
if (taskFired)
task.run();
} catch(InterruptedException e) {
}
}
}
添加任务过程
添加任务的,不论是那种模式最终都是需要通过sched方法来添加到队列当中
private void sched(TimerTask task, long time, long period) {
//校验时间
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
//约束period,防止过大
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");
//设置任务的参数
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
}
//添加到任务队列中
queue.add(task);
if (queue.getMin() == task)
//唤醒TimerThread的等待
queue.notify();
}
}
注意:
period参数,fixed delay 传入的是一个小于负数进去,fixed rate传入了一个正数进去,所以在mainLoop方法中,重新设置下次执行时间点,加入了一些判断,用来区分2种模式
TaskQueue小顶堆
class TaskQueue {
//
//
//
private TimerTask[] queue = new TimerTask[128];
//添加
void add(TimerTask task) {
// 扩容
if (size + 1 == queue.length)
queue = Arrays.copyOf(queue, 2*queue.length);
queue[++size] = task;
fixUp(size);
}
//移除
void removeMin() {
queue[1] = queue[size];
//删除额外的引用,防止内存泄露
queue[size--] = null;
fixDown(1);
}
//自上而下堆化
private void fixDown(int k) {
int j;
while ((j = k << 1) <= size && j > 0) {
if (j < size &&
queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)
j++; // j indexes smallest kid
if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
break;
TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
k = j;
}
}
//自下而上堆化
private void fixUp(int k) {
while (k > 1) {
int j = k >> 1;
if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
break;
TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
k = j;
}
}
}
Priority queue represented as a balanced binary heap: the two children of queue[n] are queue[2n] and queue[2n+1]. The priority queue is ordered on the nextExecutionTime field: The TimerTask with the lowest nextExecutionTime is in queue[1] (assuming the queue is nonempty). For each node n in the heap, and each descendant of n, d, n.nextExecutionTime <= d.nextExecutionTime.
队列的优先级用平衡二叉树来表示,queue[n]的2个孩子分别是queue[2*n]和queue[2*n+1],优先级顺序按照nextExecutionTime排序,小的放在最前面(非空)。对于堆中的每个节点n,以及n、d、n.nextExecutionTime <= d.nextExecutionTime。则此队列是基于小顶堆实现。