📄 timeout.java
字号:
/*------------------------------------------------------------------------------ Name: Timeout.java Project: xmlBlaster.org Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file Comment: Allows you be called back after a given delay. ------------------------------------------------------------------------------*/package org.xmlBlaster.util;import java.util.TreeMap;import java.util.NoSuchElementException;import org.xmlBlaster.util.def.ErrorCode;/** * Allows you be called back after a given delay. * <p /> * Note that this class should be called Timer, but with JDK 1.3 there will be a * java.util.Timer. * <p /> * There is a single background thread that is used to execute the * I_Timeout.timeout() callback. Timer callbacks should complete quickly. If a * timeout() takes excessive time to complete, it "hogs" the timer's task * execution thread. This can, in turn, delay the execution of subsequent tasks, * which may "bunch up" and execute in rapid succession when (and if) the * offending task finally completes. * <p /> * This singleton is thread-safe. * <p /> * This class does not offer real-time guarantees, but usually notifies you * within ~ 20 milliseconds of the scheduled time. * <p /> * Adding or removing a timer is good performing, also when huge amounts of * timers (> 1000) are used.<br /> * Feeding of 10000: 10362 adds/sec and all updates came in 942 millis (600MHz * Linux PC with Sun JDK 1.3.1) * * <p /> * TODO: Use a thread pool to dispatch the timeout callbacks. * <p /> * Example:<br /> * * <pre> * public class MyClass implements I_Timeout { * ... * Timeout timeout = new Timeout("TestTimer"); * Timestamp timeoutHandle = timeout.addTimeoutListener(this, 4000L, "myTimeout"); * ... * public void timeout(Object userData) { * // userData contains String "myTimeout" * System.out.println("Timeout happened"); * ... * // If you want to activate the timer again: * timeoutHandle = timeout.addTimeoutListener(this, 4000L, "myTimeout"); * } * ... * // if you want to refresh the timer: * timeoutHandle = timeout.refreshTimeoutListener(timeoutHandle, 1500L); * ... * } * </pre> * * Or a short form: * * <pre> * Timeout timeout = new Timeout("TestTimer"); * * Timestamp timeoutHandle = timeout.addTimeoutListener(new I_Timeout() { * public void timeout(Object userData) { * System.out.println("Timeout happened"); * System.exit(0); * } * }, 2000L, null); * </pre> * * JDK 1.2 or higher only. * * @author xmlBlaster@marcelruff.info * @see org.xmlBlaster.test.classtest.TimeoutTest */public class Timeout extends Thread { /** Name for logging output */ private static String ME = "Timeout"; /** Sorted map */ private TreeMap map = null; /** Start/Stop the Timeout manager thread */ private boolean running = true; /** On creation wait until thread started */ private boolean ready = false; /** Switch on debugging output */ private final boolean debug = false; /** Hold only weak reference on callback object? */ private final boolean useWeakReference; /** To avoid sync */ private boolean mapHasNewEntry; /** * Create a timer thread with a strong reference on the callback objects. */ public Timeout() { this("Timeout-Thread", false); } /** * Create a timer thread with a strong reference on the callback objects. * * @param name * The name of the thread */ public Timeout(String name) { this(name, false); } /** * @param name * The name of the thread * @param useWeakReference * If true the reference on your I_Timeout implementation is only * weak referenced and may be garbage collected even that we hold a * weak reference. */ public Timeout(String name, boolean useWeakReference) { super(name); this.useWeakReference = useWeakReference; this.map = new TreeMap(); setDaemon(true); start(); while (!ready) { // We block until our timer thread is ready try { Thread.sleep(1); } catch (InterruptedException e) { } } } /** * Get number of current used timers. * * @return The number of active timers */ public int getSize() { synchronized (map) { return map.size(); } } /** * Starts the Timeout manager thread. */ public void run() { Container container; while (running) { long delay = 100000; // sleep veeery long container = null; synchronized (map) { try { Timestamp nextWakeup = (Timestamp) map.firstKey(); // throws // exception // if empty long next = nextWakeup.getMillis(); long current = System.currentTimeMillis(); delay = next - current; if (delay <= 0) { container = (Container) map.remove(nextWakeup); if (debug) { long time = System.currentTimeMillis(); long diff = time - nextWakeup.getMillis(); System.out .println("Timeout occurred, calling listener with real time error of " + diff + " millis"); } } } catch (NoSuchElementException e) { if (debug) System.out .println("The listener map is empty, nothing to do."); } this.mapHasNewEntry = false; } if (container != null) { I_Timeout callback = container.getCallback(); // System.out.println("useWeakReference=" + useWeakReference + " // callback=" + callback); if (callback != null) { callback.timeout(container.getUserData()); } continue; } if (delay > 0) { try { synchronized (this) { ready = true; // only needed on thread creation / startup if (!this.mapHasNewEntry) { // If in the sync gap (~5 lines // higher) a new timer was // registered we need to loop // again and recalculate the // delay wait(delay); } } } catch (InterruptedException i) { // Wakeing up, and check if there is something to do } } } } /** * Add a listener which gets informed after 'delay' milliseconds. * <p /> * After the timeout happened, you are not registered any more. If you want * to cycle timeouts, you need to register again. * <p /> * * @param listener * Your callback handle (you need to implement this interface). * @param delay * The timeout in milliseconds. You can pass 0L and the Timeout * thread will fire immediately, this can be useful to dispatch a * task to the timeoutlistener * @param userData * Some arbitrary data you supply, it will be routed back to you * when the timeout occurs through method I_Timeout.timeout(). * @return A handle which you can use to unregister with * removeTimeoutListener(). */ public final Timestamp addTimeoutListener(I_Timeout listener, long delay, Object userData) { if (listener == null) { throw new IllegalArgumentException(ME + ": addTimeoutListener() with listener=null"); } Timestamp key = null; // if (delay < 1) System.out.println(ME + ": addTimeoutListener with delay // = " + delay); int nanoCounter = 0; long timeMillis = System.currentTimeMillis(); synchronized (map) { while (true) { long endNanos = (timeMillis + delay) * Timestamp.MILLION + nanoCounter; key = new Timestamp(endNanos); Object obj = map.get(key); if (obj == null) { map.put(key, new Container(this.useWeakReference, listener, userData)); break; } else { nanoCounter++; // We loop to avoid two similar keys, this should // happen very seldom // System.out.println("Looping nanoCounter=" + nanoCounter); } } this.mapHasNewEntry = true; } synchronized (this) { notify(); } return key; } /** * Refresh a listener before the timeout happened. * <p />
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -