java定时器的原理_Java Timer定时器原理

做项目很多时候会用到定时任务,比如在深夜,流量较小的时候,做一些统计工作。早上定时发送邮件,更新数据库等。这里可以用Java的Timer或线程池实现。Timer可以实现,不过Timer存在一些问题。他起一个单线程,如果有异常产生,线程将退出,整个定时任务就失败。

下面是一个Timer实现的定时任务Demo,会向控制台每隔一秒输出Do work...

1 importjava.util.Date;2 importjava.util.Timer;3 importjava.util.TimerTask;4

5 /**

6 * Created by gxf on 2017/6/21.7 */

8 public classTestTimer {9 public static voidmain(String[] args) {10 Timer timer = newTimer();11 Task task = newTask();12 timer.schedule(task, new Date(), 1000);13 }14 }15

16 class Task extendsTimerTask{17

18 @Override19 public voidrun() {20 System.out.println("Do work...");21 }22 }

控制台输出

Do work...

Do work...

Do work...

Do work...

我们将进入JDK源码分析一下,Timer原理

Timer源码

public classTimer {/*** The timer task queue. This data structure is shared with the timer

* thread. The timer produces tasks, via its various schedule calls,

* and the timer thread consumes, executing timer tasks as appropriate,

* and removing them from the queue when they're obsolete.*/

private final TaskQueue queue = newTaskQueue();/*** The timer thread.*/

private final TimerThread thread = new TimerThread(queue);

这里可以看出,有一个队列(其实是个最小堆),和一个线程对象

我们在看一下Timer的构造函数

/*** Creates a new timer. The associated thread does not

* {@linkplainThread#setDaemon run as a daemon}.*/

publicTimer() {this("Timer-" +serialNumber());

}

这里调用了有参构造函数,进入查看

/*** Creates a new timer whose associated thread has the specified name.

* The associated thread does not

* {@linkplainThread#setDaemon run as a daemon}.

*

*@paramname the name of the associated thread

*@throwsNullPointerException if {@codename} is null

*@since1.5*/

publicTimer(String name) {

thread.setName(name);

thread.start();

}

这里可以看到,起了一个线程

ok,我们再看一下,TimerTask这个类

/*** A task that can be scheduled for one-time or repeated execution by a Timer.

*

*@authorJosh Bloch

*@seeTimer

*@since1.3*/

public abstract class TimerTask implements Runnable {

虽然代码不多,也不贴完,这里看出,是一个实现了Runable接口的类,也就是说可以放到线程中运行的任务

这里就清楚了,Timer是一个线程,TimerTask是一个Runable实现类,那只要提交TimerTask对象就可以运行任务了。

public void schedule(TimerTask task, Date firstTime, longperiod) {if (period <= 0)throw new IllegalArgumentException("Non-positive period.");

sched(task, firstTime.getTime(),-period);

}

进入Timer shed(task, firstTime, period)

private void sched(TimerTask task, long time, longperiod) {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 newIllegalStateException("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.add(task)将任务放到最小堆里面,并queue.notity()唤醒在等待的线程

那么我们进入Timer类的TimerThread对象查看run方法,因为Timer类里面有个TimerThread 对象是一个线程

public voidrun() {try{

mainLoop();

}finally{//Someone killed this Thread, behave as if Timer cancelled

synchronized(queue) {

newTasksMayBeScheduled= false;

queue.clear();//Eliminate obsolete references

}

}

}

这里可以看出,在执行一个mainLoop()循环,进入这个循环

/*** The main timer loop. (See class comment.)*/

private voidmainLoop() {while (true) {try{

TimerTask task;booleantaskFired;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

longcurrentTime, 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) {

}

}

这里忘了说明,TimerTask是按nextExecutionTime进行堆排序的。每次取堆中nextExecutionTime和当前系统时间进行比较,如果当前时间大于nextExecutionTime则执行,如果是单次任务,会将任务从最小堆,移除。否则,更新nextExecutionTime的值

至此,Timer定时任务原理基本理解,单线程 + 最小堆 + 不断轮询

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值