📄 timer.java
字号:
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(); } } /** * Terminates this timer, discarding any currently scheduled tasks. * Does not interfere with a currently executing task (if it exists). * Once a timer has been terminated, its execution thread terminates * gracefully, and no more tasks may be scheduled on it. * * <p>Note that calling this method from within the run method of a * timer task that was invoked by this timer absolutely guarantees that * the ongoing task execution is the last task execution that will ever * be performed by this timer. * * <p>This method may be called repeatedly; the second and subsequent * calls have no effect. */ public void cancel() { synchronized (queue) { thread.newTasksMayBeScheduled = false; queue.clear(); queue.notify(); // In case queue was already empty. } }}/** * This "helper class" implements the timer's task execution thread, which * waits for tasks on the timer queue, executions them when they fire, * reschedules repeating tasks, and removes cancelled tasks and spent * non-repeating tasks from the queue. * <p> * The thread will timeout if no TimerTasks are scheduled for it within * a timeout period. When it times out the thread exits leaving * the newTasksMayBeScheduled * boolean true. If true and the thread is not alive it should be restarted as * in the Timer.sched method above. */class TimerThread extends Thread { /** * This flag is set to false by the reaper to inform us that there * are no more live references to our Timer object. Once this flag * is true and there are no more tasks in our queue, there is no * work left for us to do, so we terminate gracefully. Note that * this field is protected by queue's monitor! */ boolean newTasksMayBeScheduled = true; /** * Our Timer's queue. We store this reference in preference to * a reference to the Timer so the reference graph remains acyclic. * Otherwise, the Timer would never be garbage-collected and this * thread would never go away. */ private TaskQueue queue; /** * The number of milliseconds to wait after the timer queue is empty * before the thread exits. It will be restarted when the next TimerTask * is inserted. */ private static final long THREAD_TIMEOUT = 30*1000L; /** * initialize the timer thread with a task queue. * @param queue queue of tasks for this timer thread. */ TimerThread(TaskQueue queue) { this.queue = queue; } /** start the main processing loop. */ public void run() { try { mainLoop(); /* * If mainLoop returns then thread timed out with no events * in the queue. The thread will quietly be restarted in sched() * when the next TimerTask is queued. */ } catch (Throwable t) { // 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 // But no more than timeout value. while (queue.isEmpty() && newTasksMayBeScheduled) { queue.wait(THREAD_TIMEOUT); if (queue.isEmpty()) { break; } } 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 try { task.run(); } catch (Exception e) { // Cancel tasks that cause exceptions task.cancel(); } } } catch (InterruptedException e) { } } }}/** * This class represents a timer task queue: a priority queue of TimerTasks, * ordered on nextExecutionTime. Each Timer object has one of these, which it * shares with its TimerThread. Internally this class uses a heap, which * offers log(n) performance for the add, removeMin and rescheduleMin * operations, and constant time performance for the getMin operation. */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[4]; /** * The number of tasks in the priority queue. (The tasks are stored in * queue[1] up to queue[size]). */ private int size = 0; /** * Adds a new task to the priority queue. * @param task to add to the current queue */ void add(TimerTask task) { // Grow backing store if necessary if (++size == queue.length) { TimerTask[] newQueue = new TimerTask[2*queue.length]; System.arraycopy(queue, 0, newQueue, 0, size); queue = newQueue; } queue[size] = task; fixUp(size); } /** * Return the "head task" of the priority queue. (The head task is an * task with the lowest nextExecutionTime.) * @return the minimum head task of the queue. */ TimerTask getMin() { return queue[1]; } /** * Remove the head task from the priority queue. */ void removeMin() { queue[1] = queue[size]; queue[size--] = null; // Drop extra reference to prevent memory leak fixDown(1); } /** * Sets the nextExecutionTime associated with the head task to the * specified value, and adjusts priority queue accordingly. * @param newTime new time to apply to head task excution. */ void rescheduleMin(long newTime) { queue[1].nextExecutionTime = newTime; fixDown(1); } /** * Returns true if the priority queue contains no elements. * @return true if queue is empty. */ boolean isEmpty() { return size == 0; } /** * Removes all elements from the priority queue. */ void clear() { // Null out task references to prevent memory leak for (int i = 1; i <= size; i++) queue[i] = null; size = 0; } /** * Establishes the heap invariant (described above) assuming the heap * satisfies the invariant except possibly for the leaf-node indexed by k * (which may have a nextExecutionTime less than its parent's). * * This method functions by "promoting" queue[k] up the hierarchy * (by swapping it with its parent) repeatedly until queue[k]'s * nextExecutionTime is greater than or equal to that of its parent. * @param k index of queued task to be promoted up in the queue. */ 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; } } /** * Establishes the heap invariant (described above) in the subtree * rooted at k, which is assumed to satisfy the heap invariant except * possibly for node k itself (which may have a nextExecutionTime greater * than its children's). * * This method functions by "demoting" queue[k] down the hierarchy * (by swapping it with its smaller child) repeatedly until queue[k]'s * nextExecutionTime is less than or equal to those of its children. * @param k index of queued task to be demoted in the queue. */ private void fixDown(int k) { int j; while ((j = k << 1) <= size) { 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; } }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -