📄 hsqltimer.java
字号:
return false; } } /** * Retrieves whether the specified argument references a task scheduled * for periodic execution. * * @param task a task reference * @return true if the task is scheduled for periodic execution */ public static boolean isPeriodic(final Object task) { return (task instanceof Task) ? (((Task) task).period > 0) : false; } /** * Retrieves the last time the referenced task was executed, as a * Date object. If the task has never been executed, null is returned. * * @param task a task reference * @return the last time the referenced task was executed; null if never */ public static Date getLastScheduled(Object task) { if (task instanceof Task) { final Task ltask = (Task) task; final long last = ltask.getLastScheduled(); return (last == 0) ? null : new Date(last); } else { return null; } } /** * Sets the periodicity of the designated task to a new value. <p> * * If the designated task is cancelled or the new period is identical to the * task's current period, then this invocation has essentially no effect * and the submitted object is returned. <p> * * Otherwise, if the new period is greater than the designated task's * current period, then a simple assignment occurs and the submittted * object is returned. <p> * * If neither case holds, then the designated task is cancelled and a new, * equivalent task with the new period is scheduled for immediate first * execution and returned to the caller. <p> * * @return a task reference, as per the rules stated above. * @param task the task whose periodicity is to be set * @param period the new period */ public static Object setPeriod(final Object task, final long period) { return (task instanceof Task) ? ((Task) task).setPeriod(period) : task; } /** * Retrieves the next time the referenced task is due to be executed, as a * Date object. If the referenced task is cancelled, null is returned. * * @param task a task reference * @return the next time the referenced task is due to be executed */ public static Date getNextScheduled(Object task) { if (task instanceof Task) { final Task ltask = (Task) task; final long next = ltask.isCancelled() ? 0 : ltask.getNextScheduled(); return next == 0 ? null : new Date(next); } else { return null; } } /** * Adds to the task queue a new Task object encapsulating the supplied * Runnable and scheduling arguments. * * @param first the time of the task's first execution * @param runnable the Runnable to execute * @param period the task's periodicity * @param relative if true, use fixed rate else use fixed delay scheduling * @return an opaque reference to the internal task */ protected Task addTask(final long first, final Runnable runnable, final long period, boolean relative) { if (this.isShutdown) { throw new IllegalStateException("shutdown"); } final Task task = new Task(first, runnable, period, relative); // sychronized this.taskQueue.addTask(task); // sychronized this.restart(); return task; } /** Sets the background thread to null. */ protected synchronized void clearThread() {//#ifdef JAVA2FULL try { taskRunnerThread.setContextClassLoader(null); } catch (Throwable t) {}//#endif JAVA2FULL taskRunnerThread = null; } /** * Retrieves the next task to execute, or null if this timer is shutdown, * the current thread is interrupted, or there are no queued tasks. * * @return the next task to execute, or null */ protected Task nextTask() { try { while (!this.isShutdown || Thread.interrupted()) { long now; long next; long wait; Task task; // synchronized to ensure removeTask // applies only to the peeked task, // when the computed wait <= 0 synchronized (this.taskQueue) { task = this.taskQueue.peekTask(); if (task == null) { // queue is empty break; } now = System.currentTimeMillis(); next = task.next; wait = (next - now); if (wait > 0) { // release ownership of taskQueue monitor and await // notification of task addition or cancellation, // at most until the time when the peeked task is // next supposed to execute this.taskQueue.park(wait); continue; // to top of loop } else { this.taskQueue.removeTask(); } } long period = task.period; if (period > 0) { // repeated task if (task.relative) { // using fixed rate shceduling final long late = (now - next); if (late > period) { // ensure that really late tasks don't // completely saturate the head of the // task queue period = 0; // TODO: is -1, -2 ... fairer? } else if (late > 0) { // compensate for scheduling overruns period -= late; } } task.updateSchedule(now, now + period); this.taskQueue.addTask(task); } return task; } } catch (InterruptedException e) { //e.printStackTrace(); } return null; } /** * stats var */ static int nowCount = 0; /** * Convenience method replacing the longer incantation: * System.currentTimeMillis() * * @return System.currentTimeMillis() */ private static long now() { nowCount++; return System.currentTimeMillis(); } /** * The Runnable that the background thread uses to execute * scheduled tasks. <p> * * <b>Note:</b> Outer class could simply implement Runnable, * but using an inner class protects the public run method * from potential abuse. */ protected class TaskRunner implements Runnable { /** * Runs the next available task in the background thread. <p> * * When there are no available tasks, the background * thread dies and its instance field is cleared until * tasks once again become available. */ public void run() { try { do { final Task task = HsqlTimer.this.nextTask(); if (task == null) { break; } // PROBLEM: If the runnable throws an exception other // than InterruptedException (which likely stems // naturally from calling shutdownImmediately() // or getThread().interrupt()), this will still // cause the loop to exit, which is to say that // task scheduling will stop until a new task is // added or the timer is restarted directly, even // though there may still be uncancelled tasks // left on the queue. // // TODO: Clarify and establish a contract regarding // the difference between InterruptedException, // RuntimeException and other things, like // UndeclaredThrowableException. // // SOL'N: At present, we simply require each runnable to // understand its part of the implicit contract, // which is to deal with exceptions internally // (not throw them up to the timer), with the // possible exception of InterruptedException. // // If the integrity of work performed by the // runnable may be adversely affected by an // unplanned interruption, the runnable should // deal with this directly, for instance by // catching the InterruptedException, ensuring // that some integrity preserving state is // attained, and then rethrowing the exception. task.runnable.run(); } while (true); } finally { HsqlTimer.this.clearThread(); } } } /** * Encapsulates a Runnable and its scheduling attributes. * * Essentially, a wrapper class used to schedule a Runnable object * for execution by the enclosing HsqlTimer's TaskRunner in a * background thread. */ protected class Task { /** What to run. */ Runnable runnable; /** The periodic interval, or 0 if one-shot. */ long period; /** The time this task was last executed, or 0 if never. */ long last; /** The next time this task is scheduled to execute. */ long next; /** * Whether to silently remove this task instead of running it, * the next time (if ever) it makes its way to the head of the * timer queue. */ boolean cancelled = false; /** Serializes concurrent access to the cancelled field. */ private Object cancel_mutex = new Object(); /** * Scheduling policy flag. <p> * * When true, scheduling is fixed rate (as opposed to fixed delay), * and schedule updates are calculated relative to when the task was * was last run rather than a fixed delay starting from the current * wall-clock time provided by System.currentTimeMillis(). <p> * * This helps normalize scheduling for tasks that must attempt to * maintain a fixed rate of execution. */ final boolean relative; /** * Constructs a new Task object encapulating the specified Runnable * and scheduling arguments. * * @param first the first time to execute * @param runnable the Runnable to execute * @param period the periodicity of execution * @param relative if true, use fixed rate scheduling else fixed delay */ Task(final long first, final Runnable runnable, final long period, final boolean relative) { this.next = first; this.runnable = runnable; this.period = period; this.relative = relative; } // fixed reported race condition /** Sets this task's cancelled flag true and signals its taskQueue. */ void cancel() { boolean signalCancelled = false; synchronized (cancel_mutex) { if (!cancelled) { cancelled = signalCancelled = true; } } if (signalCancelled) { HsqlTimer.this.taskQueue.signalTaskCancelled(this); } } /** * Retrieves whether this task is cancelled. * * @return true if cancelled, else false */ boolean isCancelled() { synchronized (cancel_mutex) { return cancelled; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -