Java自己的Timer定时器
使用方法
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("爱陈海娥");
}
}, 1000, 1000);
}
如图所示java定时器使用还是很简单的,schedule方法接收三个参数,第一个参数 定时任务Job实现类、第二个参数是说这个job在延迟多少毫米后执行,第三个参数是说循环周期多少毫秒执行。
TimerTasker 任务Job类
这个类作为任务类首先需要考虑的就是线程安全问题, 比如一个TimerTasker实例 应该具有的是和定时任务有关的一些设计, 比如状态,下一次执行时间, 这个都是同一个实例是共享的, 所以我们加锁来保证不会出现并发修改问题, 最好的实践方法就是资源绑定锁对象,这样同一个实例搞一个锁,就可以实现多个线程公用一个锁对象
public abstract class TimerTask implements Runnable {
/**
* This object is used to control access to the TimerTask internals. 持有锁的对象
*/
final Object lock = new Object();
}
循环执行任务线程 TimerThread
要实现这个循环处理,是不能使用当前线程去做这件事情,要不然就阻塞了循环,所以需要开辟一个新的线程去做这件事, TimerThread 故继承了Thread, 即为实现多线程的一种方式。
public void run() {
try {
mainLoop(); //循环执行任务队列的jobTask
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) { //获取任务队列的锁
newTasksMayBeScheduled = false; //没有任务需要调度
queue.clear(); // Eliminate obsolete references清空任务队列
}
}
}
mainLoop
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())//队列为空 跳出循环终止, 注意来到这里说明newTasksMayBeScheduled为
break; // Queue is empty and will forever remain; die 注意来到这里说明newTasksMayBeScheduled为false
// 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(); //执行run方法,注意:这里是没有加锁的,所以我们的定时任务业务代码需要考虑线程安全问题
} catch(InterruptedException e) {
}
}
}
}
TimerThread的启动时机,在创建Timer对象的时候会启动调用TimerThread的start()方法
schedule 给Timer添加任务
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)//任务队列的最早执行任务是 当前task
queue.notify(); //唤醒任务队列的线程
}
}
总结
TImer整体逻辑实现还是比较容易看明白的, 关键就是确定共享资源的范围已经如何加锁。