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]
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); } } }
Get Java Examples in a Nutshell, 3rd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.