4.5 Timers

Java 1.3 introduced the java.util.Timer class and the abstract java.util.TimerTask class. If you subclass TimerTask and implement its run( ) method, you can then use a Timer object to schedule invocations of that run( ) method at a specified time or at multiple times at a specified interval. One Timer object can schedule and invoke many TimerTask objects. Timer is quite useful, as it simplifies many programs that would otherwise have to create their own threads to provide the same functionality. Note that java.util.Timer is not at all the same as the Java 1.2 class javax.swing.Timer.

Examples Example 4-5 and Example 4-6 are simple implementations of the TimerTask and Timer classes that can be used prior to Java 1.3. They implement the same API as the Java 1.3 classes, except that they are in the je3.thread package instead of the java.util package. These implementations are not intended to be as robust as the official implementations in Java 1.3, but they are useful for simple tasks and are a good example of a nontrivial use of threads. Note in particular the use of wait( ) and notify( ) in Example 4-6. After studying these examples, you may be interested to compare them to the implementations that come with Java 1.3.[1]

[1] If you have the Java SDK™ from Sun, look in the src.jar archive that comes with it.

Example 4-5. TimerTask.java
package je3.thread; /**  * This class implements the same API as the Java 1.3 java.util.TimerTask.  * Note that a TimerTask can only be scheduled on one Timer at a time, but  * that this implementation does not enforce that constraint.  **/ public abstract class TimerTask implements Runnable {     boolean cancelled = false;    // Has it been cancelled?     long nextTime = -1;           // When is it next scheduled?     long period;                  // What is the execution interval?     boolean fixedRate;            // Fixed-rate execution?     protected TimerTask( ) {  }     /**      * Cancel the execution of the task.  Return true if it was actually      * running, or false if it was already cancelled or never scheduled.      **/     public boolean cancel( ) {         if (cancelled) return false;         // Already cancelled;         cancelled = true;                    // Cancel it         if (nextTime == -1) return false;    // Never scheduled;         return true;     }     /**      * When it the timer scheduled to execute? The run( ) method can use this      * to see whether it was invoked when it was supposed to be       **/     public long scheduledExecutionTime( ) { return nextTime; }     /**      * Subclasses must override this to provide that code that is to be run.      * The Timer class will invoke this from its internal thread.      **/     public abstract void run( );     // This method is used by Timer to tell the Task how it is scheduled.     void schedule(long nextTime, long period, boolean fixedRate) {         this.nextTime = nextTime;         this.period = period;         this.fixedRate = fixedRate;     }     // This will be called by Timer after Timer calls the run method.     boolean reschedule( ) {         if (period == 0 || cancelled) return false; // Don't run it again         if (fixedRate) nextTime += period;         else nextTime = System.currentTimeMillis( ) + period;         return true;     } }
Example 4-6. Timer.java
package je3.thread; import java.util.Date; import java.util.SortedSet; import java.util.TreeSet; import java.util.Comparator; /**  * This class is a simple implementation of the Java 1.3 java.util.Timer API  **/ public class Timer {     // This sorted set stores the tasks that this Timer is responsible for.     // It uses a comparator to sort the tasks by scheduled execution time.     SortedSet tasks = new TreeSet(new Comparator( ) {             public int compare(Object a, Object b) {                 return (int)(((TimerTask)a).nextTime-((TimerTask)b).nextTime);             }             public boolean equals(Object o) { return this == o; }         });     // This is the thread the timer uses to execute the tasks.     // The TimerThread class is defined below.     TimerThread timer;     /** This constructor creates a Timer that does not use a daemon thread */     public Timer( ) { this(false); }     /** The main constructor: the internal thread is a daemon if specified */     public Timer(boolean isDaemon) {         timer = new TimerThread(isDaemon);  // TimerThread is defined below         timer.start( );                      // Start the thread running     }     /** Stop the timer thread, and discard all scheduled tasks */     public void cancel( ) {         synchronized(tasks) {     // Only one thread at a time!             timer.pleaseStop( );   // Set a flag asking the thread to stop             tasks.clear( );        // Discard all tasks             tasks.notify( );       // Wake up the thread if it is in wait( ).         }     }     /** Schedule a single execution after delay milliseconds */     public void schedule(TimerTask task, long delay) {         task.schedule(System.currentTimeMillis( ) + delay, 0, false);         schedule(task);     }     /** Schedule a single execution at the specified time */     public void schedule(TimerTask task, Date time) {         task.schedule(time.getTime( ), 0, false);         schedule(task);     }     /** Schedule a periodic execution starting at the specified time */     public void schedule(TimerTask task, Date firstTime, long period) {         task.schedule(firstTime.getTime( ), period, false);         schedule(task);     }     /** Schedule a periodic execution starting after the specified delay */     public void schedule(TimerTask task, long delay, long period) {         task.schedule(System.currentTimeMillis( ) + delay, period, false);         schedule(task);     }     /**       * Schedule a periodic execution starting after the specified delay.      * Schedule fixed-rate executions period ms after the start of the last.      * Instead of fixed-interval executions measured from the end of the last.      **/     public void scheduleAtFixedRate(TimerTask task, long delay, long period) {         task.schedule(System.currentTimeMillis( ) + delay, period, true);         schedule(task);     }     /** Schedule a periodic execution starting after the specified time */     public void scheduleAtFixedRate(TimerTask task, Date firstTime,                                     long period)     {         task.schedule(firstTime.getTime( ), period, true);         schedule(task);     }     // This internal method adds a task to the sorted set of tasks     void schedule(TimerTask task) {         synchronized(tasks) {  // Only one thread can modify tasks at a time!             tasks.add(task);   // Add the task to the sorted set of tasks             tasks.notify( );    // Wake up the thread if it is waiting         }     }     /**      * This inner class defines the thread that runs each of the tasks at their      * scheduled times      **/     class TimerThread extends Thread {         // This flag will be set true to tell the thread to stop running.         // Note that it is declared volatile, which means that it may be          // changed asynchronously by another thread, so threads must always         // read its current value, and not used a cached version.         volatile boolean stopped = false;           // The constructor         TimerThread(boolean isDaemon) { setDaemon(isDaemon); }         // Ask the thread to stop by setting the flag above         void pleaseStop( ) { stopped = true; }         // This is the body of the thread         public void run( ) {             TimerTask readyToRun = null;  // Is there a task to run right now?             // The thread loops until the stopped flag is set to true.             while(!stopped) {                 // If there is a task that is ready to run, then run it!                 if (readyToRun != null) {                      if (readyToRun.cancelled) {  // If it was cancelled, skip.                         readyToRun = null;                         continue;                     }                     // Run the task.                     readyToRun.run( );                     // Ask it to reschedule itself, and if it wants to run                      // again, then insert it back into the set of tasks.                     if (readyToRun.reschedule( ))                         schedule(readyToRun);                     // We've run it, so there is nothing to run now                     readyToRun = null;                     // Go back to top of the loop to see if we've been stopped                     continue;                 }                 // Now acquire a lock on the set of tasks                 synchronized(tasks) {                     long timeout;  // how many ms 'till the next execution?                     if (tasks.isEmpty( )) {   // If there aren't any tasks                         timeout = 0;  // Wait 'till notified of a new task                     }                     else {                         // If there are scheduled tasks, then get the first one                         // Since the set is sorted, this is the next one.                         TimerTask t = (TimerTask) tasks.first( );                         // How long 'till it is next run?                         timeout = t.nextTime - System.currentTimeMillis( );                         // Check whether it needs to run now                         if (timeout <= 0) {                             readyToRun = t;  // Save it as ready to run                             tasks.remove(t); // Remove it from the set                             // Break out of the synchronized section before                             // we run the task                             continue;                         }                     }                     // If we get here, there is nothing ready to run now,                     // so wait for time to run out, or wait 'till notify( ) is                     // called when something new is added to the set of tasks.                     try { tasks.wait(timeout); }                     catch (InterruptedException e) {  }                     // When we wake up, go back up to the top of the while loop                 }             }         }     }          /** This inner class defines a test program */     public static class Test {         public static void main(String[  ] args) {             final TimerTask t1 = new TimerTask( ) { // Task 1: print "boom"                     public void run( ) { System.out.println("boom"); }                 };             final TimerTask t2 = new TimerTask( ) { // Task 2: print "BOOM"                     public void run( ) { System.out.println("\tBOOM"); }                 };             final TimerTask t3 = new TimerTask( ) { // Task 3: cancel the tasks                     public void run( ) { t1.cancel( ); t2.cancel( ); }                 };                          // Create a timer, and schedule some tasks             final Timer timer = new Timer( );             timer.schedule(t1, 0, 500);     // boom every .5sec starting now             timer.schedule(t2, 2000, 2000); // BOOM every 2s, starting in 2s             timer.schedule(t3, 5000);       // Stop them after 5 seconds             // Schedule a final task: starting in 5 seconds, count             // down from 5, then destroy the timer, which, since it is             // the only remaining thread, will cause the program to exit.             timer.scheduleAtFixedRate(new TimerTask( ) {                     public int times = 5;                     public void run( ) {                         System.out.println(times--);                         if (times == 0) timer.cancel( );                     }                 },                                        5000,500);         }     } }


Java Examples in a Nutshell
Java Examples in a Nutshell, 3rd Edition
ISBN: 0596006209
EAN: 2147483647
Year: 2003
Pages: 285

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net