schedule与scheduleAtFixedRate之Timer源码分析
执行Timer任务调度方法有如下几种:
这些方法最后调用的都是这个方法:
private void sched(TimerTask task, long time, long period)
这个方法的作用是将task放入Timer实例的共享变量queue(TaskQueue类型)中。源码如下:
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(); } }
TaskQueue按照一个“完全二叉树堆”来进行排序,完全二叉树堆的中根节点的值最大(或者最小),每节点的值大于(或者小于)其两个子节点。
queue.add(task)时首先将该任务放到队列最后,会发生“上浮”动作,最终保持完全二叉树堆的根节点存放task.nextExecutionTime最小的那个task,如果要执行任务直接从TaskQueue的根节点获取那个值执行,执行完任务后从队列中移除。
这篇文章以图文的方式对二叉树堆算法讲解的很清晰 http://weixin.niurenqushi.com/article/2016-06-17/4326390.html,可以参考一下。
回到源码,sched(TimerTask task, long time, long period) 方法会将一个任务加入到TaskQueue队列后,如果这个任务在队列中的根节点(也就是nextExecutionTime最小的那个task),那就发出notify通知,激活其他占用了该任务由于wait()而阻塞状态的线程。
那这个线程主要涉及那一块呢?答案是Timer的线程体,源码如下:
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) { try { TimerTask task; boolean taskFired; synchronized(queue) { // Wait for queue to become non-empty,等待被notify激活 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) { // 如果period=0,则任务进行一次便结束,并移出队列 queue.removeMin(); task.state = TimerTask.EXECUTED; } else { // 设置下一次执行的时间 // task.period<0 对应 scheduleAtFixedRate 方法 // task.period>0 对应 schedule 方法 queue.rescheduleMin( task.period<0 ? currentTime - task.period : executionTime + task.period); } } } // 任务执行时间没到,等待,直到下一次执行时间大于当前时间 if (!taskFired) queue.wait(executionTime - currentTime); } // 执行任务时间到达,开始执行任务,没有加锁,并发执行 if (taskFired) // Task fired; run it, holding no locks task.run(); } catch(InterruptedException e) { } } }