之前听说 Timer 是线程安全的,代码也写得挺好,代码量也不多,我们可以通过查看 Timer 的代码来感受一下怎么写一个定时器。另外时间轮也可以用来写定时器。
目录
demo演示
下面是一个定时器的demo。
public static void main(String[] args) {
long start = System.currentTimeMillis();
System.out.println(System.currentTimeMillis());
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("一秒后打印 "+ (System.currentTimeMillis()-start));
}
},1000,1000);
}
有误差,但误差不大。
TimerThread
Timer 类中 Runnable 任务的处理都是在 TimerThread 完成的。
public class Timer {
private final TaskQueue queue = new TaskQueue();
private final TimerThread thread = new TimerThread(queue);
}
new Timer(); 的时候发生了什么?它启动了 TimerThread 。
public Timer() {
this("Timer-" + serialNumber());
}
public Timer(boolean isDaemon) {
this("Timer-" + serialNumber(), isDaemon);
}
public Timer(String name) {
thread.setName(name);
thread.start();
}
class TimerThread extends Thread {
private TaskQueue queue;
public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
}
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// Wait for queue to become non-empty
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;
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) { // Non-repeating, remove
// 不是周期任务
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
// 计算下次唤醒时间
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
task.run();
} catch(InterruptedException e) {
}
}
}
}
注意力,这个run()方法是处于一个for的无限循环里的,除非出现中断异常,否则不会结束。如果任务队列queue 为空,就进入queue对象的等待队列。queue本身就是java对象,也就是一把对象锁。显然一开始queue是空的,然后它就进入了queue的等待队列。注意了,上述代码里 synchronized代码块内部又嵌套一个synchronized代码块,获取两把不同的对象锁,这种情况下有可能会有死锁问题,但是 Timer 是线程安全的,因为获取锁的先后顺序保证了这一点,前一个synchronized 给queue加锁时避免并发修改任务队列,后一个synchronized给task.lock加锁是为了避免并发修改task.state属性,后面的两个synchronized也是这样。
下面这行代码又做了什么?
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("一秒后打印 "+ (System.currentTimeMillis()-start));
}
},1000,1000);
它加入了一个任务,并进行调度。
sched() 方法如下:
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");
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
}
queue.add(task);
if (queue.getMin() == task)
queue.notify();
}
}
显然,queue.notify()被执行了,queue对象锁的等待队列里的所有线程都会被移除,这些线程会抢锁,抢锁失败后才会被加入到queue的同步队列。这里也有两个嵌套的synchronized,意思同上。
TaskQueue
taskQueue是一个小顶堆的数组,从1开始到127,可以2倍扩容。Timer是线程安全的,它在操作 queue 数组变量的时候使用了synchronized保证线程同步
内部类 TaskQueue 如下
class TaskQueue {
/**
* Priority queue represented as a balanced binary heap: the two children
* of queue[n] are queue[2*n] and queue[2*n+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.
*/
private TimerTask[] queue = new TimerTask[128];
private int size = 0;
void add(TimerTask task) {
// Grow backing store if necessary
if (size + 1 == queue.length)
queue = Arrays.copyOf(queue, 2*queue.length);
queue[++size] = task;
fixUp(size);
}
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;
}
}
void removeMin() {
queue[1] = queue[size];
queue[size--] = null; // Drop extra reference to prevent memory leak
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;
}
}
void heapify() {
for (int i = size/2; i >= 1; i--)
fixDown(i);
}
}
TimerTask
TimerTask 就是一个可执行任务,是 Runnable 的实现类,并多了一些状态属性,比如当前状态,下次执行时间等。
public abstract class TimerTask implements Runnable {
final Object lock = new Object();
int state = VIRGIN;
static final int VIRGIN = 0;
static final int SCHEDULED = 1;
static final int EXECUTED = 2;
static final int CANCELLED = 3;
long nextExecutionTime;
long period = 0;
public boolean cancel() {
synchronized(lock) {
boolean result = (state == SCHEDULED);
state = CANCELLED;
return result;
}
}
public long scheduledExecutionTime() {
synchronized(lock) {
return (period < 0 ? nextExecutionTime + period
: nextExecutionTime - period);
}
}
}
未完待续。。。