| ||||
|
Code - Class EDU.oswego.cs.dl.util.concurrent.ClockDaemon1 /* 2 File: ClockDaemon.java 3 4 Originally written by Doug Lea and released into the public domain. 5 This may be used for any purposes whatsoever without acknowledgment. 6 Thanks for the assistance and support of Sun Microsystems Labs, 7 and everyone contributing, testing, and using this code. 8 9 History: 10 Date Who What 11 29Aug1998 dl created initial public version 12 17dec1998 dl null out thread after shutdown 13 */ 14 15 package EDU.oswego.cs.dl.util.concurrent; 16 import java.util.Comparator; 17 import java.util.Date; 18 19 /** 20 * A general-purpose time-based daemon, vaguely similar in functionality 21 * to common system-level utilities such as <code>at</code> 22 * (and the associated crond) in Unix. 23 * Objects of this class maintain a single thread and a task queue 24 * that may be used to execute Runnable commands in any of three modes -- 25 * absolute (run at a given time), relative (run after a given delay), 26 * and periodic (cyclically run with a given delay). 27 * <p> 28 * All commands are executed by the single background thread. 29 * The thread is not actually started until the first 30 * request is encountered. Also, if the 31 * thread is stopped for any reason, one is started upon encountering 32 * the next request, or <code>restart()</code> is invoked. 33 * <p> 34 * If you would instead like commands run in their own threads, you can 35 * use as arguments Runnable commands that start their own threads 36 * (or perhaps wrap within ThreadedExecutors). 37 * <p> 38 * You can also use multiple 39 * daemon objects, each using a different background thread. However, 40 * one of the reasons for using a time daemon is to pool together 41 * processing of infrequent tasks using a single background thread. 42 * <p> 43 * Background threads are created using a ThreadFactory. The 44 * default factory does <em>not</em> 45 * automatically <code>setDaemon</code> status. 46 * <p> 47 * The class uses Java timed waits for scheduling. These can vary 48 * in precision across platforms, and provide no real-time guarantees 49 * about meeting deadlines. 50 * <p>[<a HREF="http://gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html"> Introduction to this package. </a>] 51 **/ 52 53 public class ClockDaemon extends ThreadFactoryUser { 54 55 56 /** tasks are maintained in a standard priority queue **/ 57 protected final Heap heap_ = new Heap(DefaultChannelCapacity.get()); 58 59 60 protected static class TaskNode implements Comparable { 61 final Runnable command; // The command to run 62 final long period; // The cycle period, or -1 if not periodic 63 private long timeToRun_; // The time to run command 64 65 // Cancellation does not immediately remove node, it just 66 // sets up lazy deletion bit, so is thrown away when next 67 // encountered in run loop 68 69 private boolean cancelled_ = false; 70 71 // Access to cancellation status and and run time needs sync 72 // since they can be written and read in different threads 73 74 synchronized void setCancelled() { cancelled_ = true; } 75 synchronized boolean getCancelled() { return cancelled_; } 76 77 synchronized void setTimeToRun(long w) { timeToRun_ = w; } 78 synchronized long getTimeToRun() { return timeToRun_; } 79 80 81 public int compareTo(Object other) { 82 long a = getTimeToRun(); 83 long b = ((TaskNode)(other)).getTimeToRun(); 84 return (a < b)? -1 : ((a == b)? 0 : 1); 85 } 86 87 TaskNode(long w, Runnable c, long p) { 88 timeToRun_ = w; command = c; period = p; 89 } 90 91 TaskNode(long w, Runnable c) { this(w, c, -1); } 92 } 93 94 95 /** 96 * Execute the given command at the given time. 97 * @param date -- the absolute time to run the command, expressed 98 * as a java.util.Date. 99 * @param command -- the command to run at the given time. 100 * @return taskID -- an opaque reference that can be used to cancel execution request 101 **/ 102 public Object executeAt(Date date, Runnable command) { 103 TaskNode task = new TaskNode(date.getTime(), command); 104 heap_.insert(task); 105 restart(); 106 return task; 107 } 108 109 /** 110 * Excecute the given command after waiting for the given delay. 111 * <p> 112 * <b>Sample Usage.</b> 113 * You can use a ClockDaemon to arrange timeout callbacks to break out 114 * of stuck IO. For example (code sketch): 115 * <pre> 116 * class X { ... 117 * 118 * ClockDaemon timer = ... 119 * Thread readerThread; 120 * FileInputStream datafile; 121 * 122 * void startReadThread() { 123 * datafile = new FileInputStream("data", ...); 124 * 125 * readerThread = new Thread(new Runnable() { 126 * public void run() { 127 * for(;;) { 128 * // try to gracefully exit before blocking 129 * if (Thread.currentThread().isInterrupted()) { 130 * quietlyWrapUpAndReturn(); 131 * } 132 * else { 133 * try { 134 * int c = datafile.read(); 135 * if (c == -1) break; 136 * else process(c); 137 * } 138 * catch (IOException ex) { 139 * cleanup(); 140 * return; 141 * } 142 * } 143 * } }; 144 * 145 * readerThread.start(); 146 * 147 * // establish callback to cancel after 60 seconds 148 * timer.executeAfterDelay(60000, new Runnable() { 149 * readerThread.interrupt(); // try to interrupt thread 150 * datafile.close(); // force thread to lose its input file 151 * }); 152 * } 153 * } 154 * </pre> 155 * @param millisecondsToDelay -- the number of milliseconds 156 * from now to run the command. 157 * @param command -- the command to run after the delay. 158 * @return taskID -- an opaque reference that can be used to cancel execution request 159 **/ 160 public Object executeAfterDelay(long millisecondsToDelay, Runnable command) { 161 long runtime = System.currentTimeMillis() + millisecondsToDelay; 162 TaskNode task = new TaskNode(runtime, command); 163 heap_.insert(task); 164 restart(); 165 return task; 166 } 167 168 /** 169 * Execute the given command every <code>period</code> milliseconds. 170 * If <code>startNow</code> is true, execution begins immediately, 171 * otherwise, it begins after the first <code>period</code> delay. 172 * <p> 173 * <b>Sample Usage</b>. Here is one way 174 * to update Swing components acting as progress indicators for 175 * long-running actions. 176 * <pre> 177 * class X { 178 * JLabel statusLabel = ...; 179 * 180 * int percentComplete = 0; 181 * synchronized int getPercentComplete() { return percentComplete; } 182 * synchronized void setPercentComplete(int p) { percentComplete = p; } 183 * 184 * ClockDaemon cd = ...; 185 * 186 * void startWorking() { 187 * Runnable showPct = new Runnable() { 188 * public void run() { 189 * SwingUtilities.invokeLater(new Runnable() { 190 * public void run() { 191 * statusLabel.setText(getPercentComplete() + "%"); 192 * } 193 * } 194 * } 195 * }; 196 * 197 * final Object updater = cd.executePeriodically(500, showPct, true); 198 * 199 * Runnable action = new Runnable() { 200 * public void run() { 201 * for (int i = 0; i < 100; ++i) { 202 * work(); 203 * setPercentComplete(i); 204 * } 205 * cd.cancel(updater); 206 * } 207 * }; 208 * 209 * new Thread(action).start(); 210 * } 211 * } 212 * </pre> 213 * @param period -- the period, in milliseconds. Periods are 214 * measured from start-of-task to the next start-of-task. It is 215 * generally a bad idea to use a period that is shorter than 216 * the expected task duration. 217 * @param command -- the command to run at each cycle 218 * @param startNow -- true if the cycle should start with execution 219 * of the task now. Otherwise, the cycle starts with a delay of 220 * <code>period</code> milliseconds. 221 * @exception IllegalArgumentException if period less than or equal to zero. 222 * @return taskID -- an opaque reference that can be used to cancel execution request 223 **/ 224 public Object executePeriodically(long period, 225 Runnable command, 226 boolean startNow) { 227 228 if (period <= 0) throw new IllegalArgumentException(); 229 230 long firstTime = System.currentTimeMillis(); 231 if (!startNow) firstTime += period; 232 233 TaskNode task = new TaskNode(firstTime, command, period); 234 heap_.insert(task); 235 restart(); 236 return task; 237 } 238 239 /** 240 * Cancel a scheduled task that has not yet been run. 241 * The task will be cancelled 242 * upon the <em>next</em> opportunity to run it. This has no effect if 243 * this is a one-shot task that has already executed. 244 * Also, if an execution is in progress, it will complete normally. 245 * (It may however be interrupted via getThread().interrupt()). 246 * But if it is a periodic task, future iterations are cancelled. 247 * @param taskID -- a task reference returned by one of 248 * the execute commands 249 * @exception ClassCastException if the taskID argument is not 250 * of the type returned by an execute command. 251 **/ 252 public static void cancel(Object taskID) { 253 ((TaskNode)taskID).setCancelled(); 254 } 255 256 257 /** The thread used to process commands **/ 258 protected Thread thread_; 259 260 261 /** 262 * Return the thread being used to process commands, or 263 * null if there is no such thread. You can use this 264 * to invoke any special methods on the thread, for 265 * example, to interrupt it. 266 **/ 267 public synchronized Thread getThread() { 268 return thread_; 269 } 270 271 /** set thread_ to null to indicate termination **/ 272 protected synchronized void clearThread() { 273 thread_ = null; 274 } 275 276 /** 277 * Start (or restart) a thread to process commands, or wake 278 * up an existing thread if one is already running. This 279 * method can be invoked if the background thread crashed 280 * due to an unrecoverable exception in an executed command. 281 **/ 282 283 public synchronized void restart() { 284 if (thread_ == null) { 285 thread_ = threadFactory_.newThread(runLoop_); 286 thread_.start(); 287 } 288 else 289 notify(); 290 } 291 292 293 /** 294 * Cancel all tasks and interrupt the background thread executing 295 * the current task, if any. 296 * A new background thread will be started if new execution 297 * requests are encountered. If the currently executing task 298 * does not repsond to interrupts, the current thread may persist, even 299 * if a new thread is started via restart(). 300 **/ 301 public synchronized void shutDown() { 302 heap_.clear(); 303 if (thread_ != null) 304 thread_.interrupt(); 305 thread_ = null; 306 } 307 308 /** Return the next task to execute, or null if thread is interrupted **/ 309 protected synchronized TaskNode nextTask() { 310 311 // Note: This code assumes that there is only one run loop thread 312 313 try { 314 while (!Thread.interrupted()) { 315 316 // Using peek simplifies dealing with spurious wakeups 317 318 TaskNode task = (TaskNode)(heap_.peek()); 319 320 if (task == null) { 321 wait(); 322 } 323 else { 324 long now = System.currentTimeMillis(); 325 long when = task.getTimeToRun(); 326 327 if (when > now) { // false alarm wakeup 328 wait(when - now); 329 } 330 else { 331 task = (TaskNode)(heap_.extract()); 332 333 if (!task.getCancelled()) { // Skip if cancelled by 334 335 if (task.period > 0) { // If periodic, requeue 336 task.setTimeToRun(now + task.period); 337 heap_.insert(task); 338 } 339 340 return task; 341 } 342 } 343 } 344 } 345 } 346 catch (InterruptedException ex) { } // fall through 347 348 return null; // on interrupt 349 } 350 351 /** 352 * The runloop is isolated in its own Runnable class 353 * just so that the main 354 * class need not implement Runnable, which would 355 * allow others to directly invoke run, which is not supported. 356 **/ 357 358 protected class RunLoop implements Runnable { 359 public void run() { 360 try { 361 for (;;) { 362 TaskNode task = nextTask(); 363 if (task != null) 364 task.command.run(); 365 else 366 break; 367 } 368 } 369 finally { 370 clearThread(); 371 } 372 } 373 } 374 375 protected final RunLoop runLoop_; 376 377 /** 378 * Create a new ClockDaemon 379 **/ 380 381 public ClockDaemon() { 382 runLoop_ = new RunLoop(); 383 } 384 385 386 387 } 388 |
|||
Java API By Example, From Geeks To Geeks. |
Conditions of Use |
About Us
© 2002 - 2005, KickJava.com, or its affiliates
|