jdk定时任务的原理

先来看下timer的调用方式,简单的定时打印

	public static void main(String[] args){
		Timer timer = new Timer();
		timer.schedule(new TimerTask() {
			
			@Override
			public void run() {
				System.out.println(Thread.currentThread().getName());
			}
		}, 5000, 5000);
	}

运行结果:

Timer-0
Timer-0
Timer-0
Timer-0
Timer-0

然后我好奇它是如何实现定时调用的,打开TimerTask类的源码,发现它是一个对runable的封装,添加了一些状态和同步锁对象以及下次执行的时间,它封装了以下状态

 /**
     * This task has not yet been scheduled.任务还没有被调用
     */
    static final int VIRGIN = 0;

    /**
     * This task is scheduled for execution.  If it is a non-repeating task,
     * it has not yet been executed. 任务被调度去执行,如果是一个不循环执行的任务,表示它还没有被执行
     */
    static final int SCHEDULED   = 1;

    /**
     * This non-repeating task has already executed (or is currently
     * executing) and has not been cancelled.不重复执行任务已经执行或者正在执行,不能被取消
     */
    static final int EXECUTED    = 2;

    /**
     * This task has been cancelled (with a call to TimerTask.cancel).任务已经被取消,(调用 timerTask 的cancel 方法
     */
    static final int CANCELLED   = 3;

 /**

    * 下次执行的时间,System.currentTimeMillis 格式
     * Next execution time for this task in the format returned by
     * System.currentTimeMillis, assuming this task is scheduled for execution.
     * For repeating tasks, this field is updated prior to each task execution.
     */
    long nextExecutionTime;

    /**

    * 执行间隔
     * Period in milliseconds for repeating tasks.  A positive value indicates
     * fixed-rate execution.  A negative value indicates fixed-delay execution.
     * A value of 0 indicates a non-repeating task.
     */
    long period = 0;

     

   以上可以看出timer+timerTask的方式不适合使用自然时间的任务。

 

    Timer.class

   timer主要有两个关键的成员变量final修饰

    //内部类实现,timerTask的队列,封装实现了,最大128个任务

    private final TaskQueue queue = new TaskQueue();

    /**
     * The timer thread.,内部类,一个线程的封装实现
     */
    private final TimerThread thread = new TimerThread(queue);

 

   先简单介绍下TaskQueue.class,看成员变量

/**

    保存定时任务队列,可以看到最大一个timer最多可以提交128个任务
     * 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];

    /**
     * The number of tasks in the priority queue.  (The tasks are stored in
     * queue[1] up to queue[size]).
     */
    private int size = 0;

  TaskQueue提供了一系列方法是对该队列即将执行的任务的操作,获取最近要执行的人,修改任务的下次执行时间

   主要是有两个私有方法fixUp(),fixDown();用来更新队列的顺序。

 

    再来看下TimerThread.class,主要看下run方法

public void run() {
        try {           
            mainLoop();
        } finally {
            // Someone killed this Thread, behave as if Timer cancelled
//遇到异常或取消任务,清空任务队列,修改状态,不按照可以提交新的任务
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }

    /**
     * The main timer loop.  (See class comment.)
     */
    private void mainLoop() {
//使用while(true)实现线程一直在运行,而不是任务到时间了另起一个线程,所以timer只有一个线程
        while (true) {
            try {
                TimerTask task;
                boolean taskFired;
                synchronized(queue) {
                    // Wait for queue to become non-empty
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        //new timer()的时候线程已经启动了的,在这里等带任务的提交,为什么是用while呢?
                        queue.wait();
                    if (queue.isEmpty())//队列空直接退出,在停止timer的情况下
                        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) {
            }
        }
    }
}

可以看到timer的定时其实是使用一个线程while(true)和,wait(),notify()配合实现的。代码有注释不多解释了。

 

总结:

jdk timer + timertask的方式实现的定时任务,由于实现方式的局限性,导致不能支持更多的特性,多线程,自然时间等。 

       
 

 

     

 

 

 

 

转载于:https://my.oschina.net/u/2563693/blog/683167

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值