/** 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() && queue.newTasksMayBeScheduled) { queue.wait(THREAD_TIMEOUT); if (queue.isEmpty()) { break; } } if (queue.isEmpty()) break; // Queue is empty and will forever remain; die // Handle a possible change of the user clock queue.checkUserClockChange(); // 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 = Timer.monotonicTimeMillis(); 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 long timeout = executionTime - currentTime; if (queue.hasUserClockTasks() && timeout > Timer.USER_CLOCK_CHECK_PERIOD) { timeout = Timer.USER_CLOCK_CHECK_PERIOD; } queue.wait(timeout); } } 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) { } } }
/** * Schedule the specified timer task for execution at the specified time with the specified * period, in milliseconds. If period is positive, the task is scheduled for repeated execution; * if period is zero, the task is scheduled for one-time execution. Time is specified in * Date.getTime() format. This method checks timer state, task state, and initial execution time, * but not period. * * @param task task to be scheduled. * @param userTime the user time at which task is to be executed or <tt>null</tt> if the delay is * specified * @param delay the delay in milliseconds before the task execution * @param period time in milliseconds between successive task executions. * @param isUserClock true if the time is bound to user clock * @throws IllegalArgumentException if <tt>time()</tt> is negative. * @throws IllegalStateException if task was already scheduled or cancelled, timer was cancelled, * or timer thread terminated. */ private void sched(TimerTask task, Date userTime, long delay, long period) { final boolean isUserClock = userTime != null; long time; if (isUserClock) { long t = userTime.getTime(); if (t < 0) { throw new IllegalArgumentException("Illegal execution time."); } time = Timer.userTimeFromStart(t); } else { time = Timer.monotonicTimeFromStart(JVM.monotonicTimeMillis() + delay); } synchronized (queue) { if (!queue.newTasksMayBeScheduled) { throw new IllegalStateException("Timer already cancelled."); } /* * If the TimerThread has exited without an error * it is restarted. See the commentary in TimerThread.run. */ if (thread == null || !thread.isAlive()) { thread = new TimerThread(queue); thread.start(); } 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; task.isUserClock = isUserClock; } queue.add(task); if (queue.getMin() == task) queue.notify(); } }