Java Institute DreamsCity 2000
Welcome to DreamsCity
Return to Java Institute

USING TIMERS TO RUN RECURRING
OR FUTURE TASKS ON A
BACKGROUND THREAD

Many applications need to schedule tasks for future execution, or 
to schedule them to recur at a regular interval. J2SE v1.3 meets 
this need with the addition of two Timer classes: java.util.Timer 
and java.util.TimerTask. This tip demonstrates various scheduling 
strategies for using these Timer classes. The tip also shows you 
how to handle poorly-behaved tasks, that is, tasks that run too 
long or that crash.  

The java.util.Timer and java.util.TimerTask classes are simple to 
use. As with many things threaded, the TimerTask class implements 
the Runnable interface. To use the class, simply write a subclass 
with a run method that does the work; then plug the subclass into 
a Timer instance. Here's an example:

    import java.util.*;
    import java.io.*;
    
    public class TestTimers {
    
        public static void doMain() throws Exception {
            Timer t = new Timer(true);
            t.schedule(new Ping("Fixed delay"), 0, 1000);
            Thread.currentThread().sleep(12000);
        }
    
        public static void main(String[] args) {
            try { 
                doMain(); 
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    class Ping extends TimerTask  {
        private String name;
        public Ping(String name) {
            this.name = name;
        }
        public void run() {
            System.out.println(name + " Ping at " + new Date());
        }
    }

The class TestTimers creates a Timer. By passing the Timer the
boolean value true, TestTimers forces the Timer to use a daemon 
thread. The main thread then sleeps, allowing you see the Timer 
at work. However you never actually see any thread classes or 
instances; those details are encapsulated by the Timer class. 

In the statement,

           t.schedule(new Ping("Fixed delay"), 0, 1000);
           
the parameters to the schedule method cause a Ping object's run 
method to be invoked after an initial delay of 0 milliseconds; 
the method is repeatedly invoked every 1000 milliseconds. Ping's 
run method logs output to System.out. (In your own applications, 
you would use the run method to do something more interesting.)  
If you run TestTimers, you will see output similar to this: 

    Fixed delay ping at Thu May 18 14:18:56 EDT 2000
    Fixed delay ping at Thu May 18 14:18:57 EDT 2000
    Fixed delay ping at Thu May 18 14:18:58 EDT 2000
    Fixed delay ping at Thu May 18 14:18:59 EDT 2000
    Fixed delay ping at Thu May 18 14:19:00 EDT 2000
    Fixed delay ping at Thu May 18 14:19:01 EDT 2000
    Fixed delay ping at Thu May 18 14:19:02 EDT 2000
    Fixed delay ping at Thu May 18 14:19:03 EDT 2000
    Fixed delay ping at Thu May 18 14:19:04 EDT 2000
    Fixed delay ping at Thu May 18 14:19:05 EDT 2000
    Fixed delay ping at Thu May 18 14:19:06 EDT 2000
    Fixed delay ping at Thu May 18 14:19:07 EDT 2000
    Fixed delay ping at Thu May 18 14:19:08 EDT 2000

The output confirms that Ping is running about once per second, 
exactly as requested. Better still, the Timer can handle multiple 
TimerTasks, each with different start times and repeat periods. 
This leads to an interesting question: If a TimerTask takes a very 
long time to complete, will other tasks in the list be thrown off?  

To answer this question, you need to understand how the Timer uses 
threads. Each Timer instance has a single dedicated thread that 
all the TimerTasks share. So, if one task takes a long time, all 
the other tasks wait for it to complete. Consider this long-running 
task: 

    class PainstakinglySlowTask extends TimerTask {
        public void run() {
            file://simulate some very slow activity by sleeping
            try {
                Thread.currentThread().sleep(6000);
                System.out.println("Painstaking task ran at " + new Date());
            }
            catch (InterruptedException ie) {
                Thread.currentThread().interrupt();
            }
        }
    }

The PainstakinglySlowTask class sleeps for six full seconds. It
prevents any other tasks from executing during that time. What 
happens if you add the painstakingly slow task to TestTimers? 
Let's see.

    public static void doMain() throws Exception {
        Timer t = new Timer(true);
        t.schedule(new Ping("Fixed delay"), 0, 1000);
        t.schedule(new PainstakinglySlowTask(), 2000);
        Thread.currentThread().sleep(12000);
    }

If you recompile and run TestTimers, you will see output like this:

    Fixed delay Ping at Thu May 18 15:41:33 EDT 2000
    Fixed delay Ping at Thu May 18 15:41:34 EDT 2000
    Fixed delay Ping at Thu May 18 15:41:35 EDT 2000
    Painstaking task ran at Thu May 18 15:41:41 EDT 2000
    Fixed delay Ping at Thu May 18 15:41:41 EDT 2000
    Fixed delay Ping at Thu May 18 15:41:42 EDT 2000
    Fixed delay Ping at Thu May 18 15:41:43 EDT 2000
    Fixed delay Ping at Thu May 18 15:41:44 EDT 2000
    Fixed delay Ping at Thu May 18 15:41:45 EDT 2000

During the time that PainstakinglySlowTask runs (from 15:41:35
to 15:41:41), no pings occur. This is what is meant by a "fixed 
delay". The Timer tries to make the delay between Pings as 
precise as possible, even if that means that some Pings are lost 
during the running time of another, long-running task.

A scheduling alternative is "fixed rate." With fixed rate 
scheduling, the Timer tries to make the processing rate as 
accurate as possible over time. So, if one task runs for a long 
time, other tasks can instantaneously run several times in order 
to catch up. You can specify fixed rate scheduling by using the 
scheduleAtFixedRate method:

    file://commented out the fixed delay version
    file://t.schedule(new Ping("Fixed delay"), 0, 1000);
    t.scheduleAtFixedRate(new Ping("Fixed rate"), 0, 1000);
    t.schedule(new PainstakinglySlowTask(), 2000);

If you run TestTimers with a fixed rate ping, you should 
see output like this:

    Fixed rate Ping at Thu May 18 15:48:33 EDT 2000
    Fixed rate Ping at Thu May 18 15:48:34 EDT 2000
    Fixed rate Ping at Thu May 18 15:48:35 EDT 2000
    Painstaking task ran at Thu May 18 15:48:41 EDT 2000
    Fixed rate Ping at Thu May 18 15:48:41 EDT 2000
    Fixed rate Ping at Thu May 18 15:48:41 EDT 2000
    Fixed rate Ping at Thu May 18 15:48:41 EDT 2000
    Fixed rate Ping at Thu May 18 15:48:41 EDT 2000
    Fixed rate Ping at Thu May 18 15:48:41 EDT 2000
    Fixed rate Ping at Thu May 18 15:48:41 EDT 2000
    Fixed rate Ping at Thu May 18 15:48:42 EDT 2000
    Fixed rate Ping at Thu May 18 15:48:43 EDT 2000
    Fixed rate Ping at Thu May 18 15:48:44 EDT 2000
    Fixed rate Ping at Thu May 18 15:48:45 EDT 2000

This time, several Pings run right after PainstakinglySlowTask 
finishes; the Pings all run at 15:48:41. This keeps the rate of 
Pings as close as possible to the desired 1000 msec average. The 
price paid is occasionally having Pings run at approximately the 
same time.  

Both fixed-rate and fixed-delay scheduling have their uses.  
However, neither totally eliminates the interference caused by 
long-running tasks. If you have different tasks that might run 
for a very long time, you might want to minimize the interference 
between the tasks. This is especially true if you need to take 
advantage of multiple CPUs. A single Timer provides no obvious 
way to do this. You cannot control the Timer thread, because it 
is encapsulated as a private field of the Timer class. Instead, 
you can create multiple Timers, or have one Timer call notify() 
and have other threads do the actual work.  

Tasks that throw exceptions pose more of a problem than 
long-running tasks. Here's an example. Replace the 
PainstakinglySlowTask class with the following CrashingTask class:

    class CrashingTask extends TimerTask {
        public void run() {
            throw new Error("CrashingTask");
        }
    }

    file://new version of TestTimers
    public static void doMain() throws Exception {
        Timer t = new Timer(true);
        t.scheduleAtFixedRate(new Ping("Fixed rate"), 0, 1000);
        t.schedule(new CrashingTask(), 5000, 1000);
        Thread.currentThread().sleep(12000);
    }

If you run TestTimers with CrashingTask, you should see output 
that looks something like this:

    Fixed rate Ping at Thu May 18 15:58:53 EDT 2000
    Fixed rate Ping at Thu May 18 15:58:54 EDT 2000
    Fixed rate Ping at Thu May 18 15:58:55 EDT 2000
    Fixed rate Ping at Thu May 18 15:58:56 EDT 2000
    Fixed rate Ping at Thu May 18 15:58:57 EDT 2000
    Fixed rate Ping at Thu May 18 15:58:58 EDT 2000
    java.lang.Error: CrashingTask
        at CrashingTask.run(TestTimers.java:37)
        at java.util.TimerThread.mainLoop(Timer.java:435)
        at java.util.TimerThread.run(Timer.java:385)

After CrashingTask throws an exception, it never runs again. This 
should come as no surprise. What may surprise you is that no other 
task on the same Timer will run again, either. A wayward 
exception will cancel the Timer, causing any future attempt to 
schedule a task to throw an exception. However, there is no 
mechanism to notify your existing tasks that they have been 
brutally de-scheduled. It is up to you to make sure that errant 
TimerTasks do not destroy your Timers. One strategy is to guarantee 
that your TimerTasks never throw exceptions back into the Timer. 
You can do this by enclosing the TimerTasks in a try block that 
catches the exception. If you need to be notified of the exception, 
you can create a simple mechanism to notify the program that 
a failure occurred. Here's an example: 

    import java.util.*;
    import java.io.*;
    
    interface ExceptionListener {
        public void exceptionOccurred(Throwable t);
    }
    
    class ExceptionLogger implements ExceptionListener {
        public void exceptionOccurred(Throwable t) {
            System.err.println("Exception on Timer thread!");
            t.printStackTrace();
        }
    }
    
    public class TestTimers {
    
        public static void doMain() throws Exception {
            Timer t = new Timer(true);
            file://t.schedule(new Ping("Fixed delay"), 0, 1000);
            t.scheduleAtFixedRate(new Ping("Fixed rate"), 0, 1000);
            t.schedule(new CrashingTask(new ExceptionLogger()), 5000, 5000);
            file://t.schedule(new PainstakinglySlowTask(), 2000);
            Thread.currentThread().sleep(12000);
        }
    
        public static void main(String[] args) {
            try { 
                doMain(); 
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    class Ping extends TimerTask  {
        private String name;
        public Ping(String name) {
            this.name = name;
        }
        public void run() {
            System.out.println(name + " Ping at " + new Date());
        }
    }
    
    class CrashingTask extends TimerTask {
        ExceptionListener el;
        public CrashingTask(ExceptionListener el) {
            this.el = el;
        }
    
        public void run() {
            try {
                throw new Error("CrashingTask");
            }
            catch (Throwable t) {
                cancel();
                el.exceptionOccurred(t);
            }
        }
    }

This code is very similar to the previous version, except that this 
time CrashingTask's run method never propagates exceptions of any 
type. Instead, it uses a catch block to catch all Throwables and 
then uses a callback interface to report the exception. Here's the 
output:

    Fixed rate Ping at Thu May 18 16:41:03 EDT 2000
    Fixed rate Ping at Thu May 18 16:41:04 EDT 2000
    Fixed rate Ping at Thu May 18 16:41:05 EDT 2000
    Fixed rate Ping at Thu May 18 16:41:06 EDT 2000
    Fixed rate Ping at Thu May 18 16:41:07 EDT 2000
    Fixed rate Ping at Thu May 18 16:41:08 EDT 2000
    Exception on Timer thread!
    java.lang.Error: CrashingTask
        at CrashingTask.run(TestTimers.java:54)
        at java.util.TimerThread.mainLoop(Timer.java:435)
        at java.util.TimerThread.run(Timer.java:385)
    Fixed rate Ping at Thu May 18 16:41:09 EDT 2000
    Fixed rate Ping at Thu May 18 16:41:10 EDT 2000
    Fixed rate Ping at Thu May 18 16:41:11 EDT 2000
    Fixed rate Ping at Thu May 18 16:41:12 EDT 2000
    Fixed rate Ping at Thu May 18 16:41:13 EDT 2000
    Fixed rate Ping at Thu May 18 16:41:14 EDT 2000
    Fixed rate Ping at Thu May 18 16:41:15 EDT 2000

When CrashingTask throws an exception, it calls cancel on itself 
to remove itself from the Timer. It then logs the exception by 
calling an implementation of the ExceptionListener interface.  
Because the exception never propagates back into the Timer thread, 
the Pings continue to function even after CrashingTask fails. In 
a production system, a more robust implementation of 
ExceptionListener could take action to deal with the exception 
instead of simply logging it.  

There is another Timer class in the Java Platform, 
javax.swing.Timer. Which Timer should you use? The Swing Timer is 
designed for a very specific purpose. It does work on the AWT event 
thread. Because much of the Swing package code must execute on the 
AWT event thread, you should use the Swing Timer if you are 
manipulating the user interface. For other tasks, use the 
java.util.Timer for its flexible scheduling.  

For more info on the Timer classes, see
http://java.sun.com/j2se/1.3/docs/api/java/util/Timer.html.

Any comments? email to:
richard@dreamscity.net