Home : Course Map : Chapter 8 : Java : Tech :
Timers
JavaTech
Course Map
Chapter 8

Introduction
Threads Overview
  Demo 1   Demo 2
Stopping Threads
Multi-Processing
Thread Tasks
Animations
 
 Demo 3   Demo 4  
  Demo 5

Non-interacting
  Demo 6

Task Splitting
  Demo 7

Exclusivity
  Demo 8

Communicating
  Demo 9

Priority/Scheduling
More Thread

Exercises

    Supplements
Java2D Animation
  Demo 1 
Processor View
More Concurrency
Cloning
  Demo 2  Demo 3 

     About JavaTech
     Codes List
     Exercises
     Feedback
     References
     Resources
     Tips
     Topic Index
     Course Guide
     What's New

A parallel process capability allows for timers, which provide for periodic updates and scheduling of tasks. For example, a timer can

  • Signal the redrawing of frames for an animation.
  • Prompt for the status of a task and post a message about it.
  • Issue a daily/weekly reminders.
  • Trigger a single task at a particular time in the future.

With the Thread class you could create your own timer class using the Thread.sleep(T) method to delay action for some time T (see, for example, Chapter 8: Java : Demo 1). This approach, however, has some drawbacks. For periodic events, if the processing times in between the sleep periods vary significantly, then the overall timing will vary as well and soon get out of synch with the "wall clock". Also, if you need many timer events, the program will require many threads and use up system resources.

For many applications you can instead use the timer classes that come with the Java 2 SE:

  • javax.swing.Timer provides timers to use with the Swing GUI such as prompting the update of a component like a progress bar.

  • The java.util package (as of version 1.3) includes Timer and TimerTask, which work together to provide general purpose timers.

These timers can provide multiple timed events from a single thread and thus conserve resources. They also have various useful methods such as scheduleAtFixedRate(TimerTask task, long delay, long period) in java.util.Timer. This method will set events to occur periodically at a fixed rate and tied to the system clock. This is obviously useful for many applications such as a countdown timer and alarm clocks where you don't want the timing to drift relative to absolute time.

The resources listed below include a number of tutorials and example programs dealing with timers. Here we will give a couple of simple examples of the two types of Java timers to illustrate how they work.

Note: these timers are not intended for real-time applications where the timing of actions must be predictable and guaranteed.

TimerTask and Timer

The Timer and TimerTask combo in java.util offers the most general timing capabilities with a number of options. A Timer object holds a single thread and can control many TimerTask objects. The TimerTask abstract class implements the Runnable interface but it does not provide a concrete run() method. Instead you create a TimerTask subclass to provide the concrete run() method with the code to carry out the task of interest.

In the example below we fill a simulate the filling of a histogram during an experiment. The data comes in at random times and we want to show the histogram during the "data taking". The MakeData object runs in a thread and the run() method generates values from a Gaussian and uniform random number generators to simulate "signal" and background noise, resp. MakeData uses the methods from the Updateable interface to call back to the owner to tell it that new data has been added and when the histogram filling is finished.

We use a timer to set a flag to signal that the histogram display should be repainted. A subclass of TimerTask called PaintHistTask sets the flag. Since its task is so simple we made it an inner class for convenience and clarity. A Timer object is created and scheduled to run PaintHistTask every 250 milliseconds and to begin after a 100 millisecond delay to give time for the histogram to receive some data.


TimerHistFillApplet.java - similar to UIDrawHistApplet in Chapter 7: Tech except that here a timer (java.util.Timer and its helper java.util.TimerTask) is used to allow for display of the histogram during its filling. A separate thread object, MakeData, creates the data. The class implements the Updateable interface to provide for callbacks.

+ New classes:
MakeData.java - an instance of this Runnable class fills the histogram with a combination of a Gaussian and flat background to simulate data taking. It calls back to the Updateable owner object via the update() and done() methods

Updateable.java - interface for callbacks to inform the calling object of an addition to the histogram and when the data filling has finished.

+ Previous classes:
Chapter 6:Tech: Histogram.java, HistPanel.java
Chapter 6:Tech: PlotPanel.java, PlotFormat.java

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

/**
  * Demonstrate the java.util.Timer by filling a histogram with
  * radomly generated data and periodically updating the histogram
  * display.
  *
  * This program will run as an applet inside an application frame.
  *
  * The applet uses the HistPanel to display contents of
  * an instance of Histogram. HistFormat used by HistPanel to
  * format the scale values.
  *
  * The java.util.Timer and java.util.TimerTask are used
  * to update the display of the histogram during the filling
  * of the histogram.
  *
  * Includes "Go" button to initiate the filling of the histogram.
  * To simulate data taking, a combination of a Gaussian and random
  * background values are generated.
  *
  * The number of values taken from
  * entry in a JTextField. "Clear"  button clears the histogram.
  * In standalone mode, the Exit button closes the program.
 **/
public class TimerHistFillApplet extends JApplet
             implements ActionListener, Updateable
{
  // Use the HistPanel JPanel subclass here
  HistPanel fOutputPanel = null;

  Histogram fHistogram = null;
  int fNumDataPoints = 100;

  boolean fMakingHist = false;
  boolean fUpdateDisplay = false;
  MakeData fMakeData;

  // Use the java.util Timer and TimerTask combo
  // for timing events.
  java.util.Timer fTimer;

  // A text field for input strings
  JTextField fTextField = null;

  // Flag for whether the applet is in a browser
  // or running via the main () below.
  boolean fInBrowser=true;

  //Buttons
  JButton fGoButton;
  JButton fClearButton;
  JButton fExitButton;

  /**
    * Create a User Interface with a histogram and a Go button
    * to initiate processing and a Clear button to clear the .
    * histogram. In application mode, the Exit button stops the
    * program.
   **/
  public void init () {
    Container content_pane = getContentPane ();

    JPanel panel = new JPanel (new BorderLayout ());

    // Use a textfield for an input parameter.
    fTextField =
      new JTextField (Integer.toString (fNumDataPoints), 10);

    // If return hit after entering text, the
    // actionPerformed will be invoked.
    fTextField.addActionListener (this);

    fGoButton = new JButton ("Go");
    fGoButton.addActionListener (this);

    fClearButton = new JButton ("Clear");
    fClearButton.addActionListener (this);

    fExitButton = new JButton ("Exit");
    fExitButton.addActionListener (this);

    JPanel control_panel = new JPanel ();

    control_panel.add (fTextField);
    control_panel.add (fGoButton);
    control_panel.add (fClearButton);
    control_panel.add (fExitButton);

    // Create a histogram and start filling it.
    makeHist ();
    fGoButton.setText ("Stop");
    fClearButton.setEnabled (false);
    if (fInBrowser) fExitButton.setEnabled (false);

    // JPanel subclass here.
    fOutputPanel = new HistPanel (fHistogram);

    panel.add (fOutputPanel,"Center");
    panel.add (control_panel,"South");

    // Add text area with scrolling to the contentPane.
    content_pane.add (panel);

  } // init

  /** Stop the filling if the browser page unloaded. **/
  public void stop () {
    fGoButton.setText ("Go");
    fClearButton.setEnabled (true);
    fMakingHist = false;
  }

  public void actionPerformed (ActionEvent e) {
    Object source = e.getSource ();
    if (source == fGoButton || source == fTextField) {
          String strNumDataPoints = fTextField.getText ();
          try
          {
        fNumDataPoints = Integer.parseInt (strNumDataPoints);
      }
      catch (NumberFormatException ex)
      {   // Could open an error dialog here but just
          // display a message on the browser status line.
        showStatus ("Bad input value");
        return;
      }
      if (!fMakingHist) {
          makeHist ();
          fGoButton.setText ("Stop");
          fClearButton.setEnabled (false);
      }  else {
          // Stop button has been pushed
          fGoButton.setText ("Go");
          fClearButton.setEnabled (true);
          fMakingHist = false;
      }

    } else if (source == fClearButton) {
        fHistogram.clear ();
        repaint ();
    } else if (!fInBrowser)
        System.exit (0);
  } // actionPerformed


  void makeHist () {
    if (fMakingHist) return; // only fill one hist at a time.
    fMakingHist = true;

    // Create an instance of the histogram class.
    // Make it wide enough enough to include the data.
    if (fHistogram == null)
        fHistogram = new Histogram ("Gaussian + Random Background",
                                "Data",
                                20,-10.0,10.0);

    // Create the runnable object to fill the histogram
    // Center signal at 3.0 and create background between
    // -10 and 10. The fraction of the data due to the
    // Gaussian will be 0.60. The maximum delay between
    // data poins will be 500msecs.
    fMakeData =
      new MakeData (this, fHistogram, fNumDataPoints,
                   3.0, 0.60, -10.0, 10.0, 500);
    Thread data_thread = new Thread (fMakeData);


    // Before starting the filling, create the timer task
    // that will cause the histogram display to update
    // during the filling.
    // Create a timer. TimerTask created in MakeHist ()
    fTimer = new java.util.Timer ();

    // Start the timer after 100ms and then repeat calls
    // to run in PaintHistTask object every 250ms.
    fTimer.schedule (new PaintHistTask (), 100, 250);

    // Now start the data filling.
    data_thread.start ();

  } // makeHist

  /**
    * Use the inner class technique to define the
    * TimerTask subclass for signalling that the display
    * should be updated.
   **/
  class PaintHistTask extends java.util.TimerTask
  {
    public void run () {
       fUpdateDisplay = true;
    }
  } // class PaintHistTask

  /**
    *  Repaint the histogram display and turn off the
    *  update display flag. Return the fMakingHist flag
    *  to indicate if updating should continue.
   **/
  public boolean update (Object obj) {
    // Don't update the display until the timer
    // turns on the fUpdateDisplay flag.
    if (fUpdateDisplay) {
      // Possible this method called before fOutputPanel
      // created in the init (). So check if it exists
      // before attempting to repaint.
      if (fOutputPanel != null) fOutputPanel.repaint ();
      fUpdateDisplay = false;
    }
    return fMakingHist;
  } // update

  /** Called when the histogram filling is finished. **/
  public void done () {
    fMakingHist = false;

    // Stop the histogram display updates.
    fTimer.cancel ();

    // Update one last time
    fOutputPanel.repaint ();

    // Reset the buttons.
    fGoButton.setText ("Go");
    fClearButton.setEnabled (true);
  } .. done

  public static void main (String[] args) {
    //
    int frame_Width=450;
    int frame_Height=300;

    //
    TimerHistFillApplet applet = new TimerHistFillApplet ();
    applet.fInBrowser = false;
    applet.init ();

    // Following anonymous class used to close window & exit program
    JFrame f = new JFrame ("Demo");
    f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

    // Add applet to the frame
    f.getContentPane ().add ( applet);
    f.setSize (new Dimension (frame_Width,frame_Height));
    f.setVisible (true);
  } //main
} // class TimerHistFillApplet
/**
  * This runnable class fills a histogram with Gaussian
  * distributed random data.
 **/
public class MakeData implements Runnable
{
  Histogram fHistogram;
  int fNumData;
  Updateable fOwner;

  // Gaussian center with default value
  double fSignalPoint;

  // Flat background range with defaults
  double fBgLow;
  double fBgRange;

  // Fraction of the data due to the Gaussian
  double fFractionSignal = 0.60;

  // Maximum delay between data points with default
  int fMaxDelay = 1000;

  // Create an instance of the Random class for
  // producing our random values.
  static java.util.Random fRan = new java.util.Random ();

/**
  *  Constructor
  *  @param owner to call back with updates.
  *  @param histogram to add data to.
  *  @param numData Number of data points to add to histogram.
  *  @param signalPoint Position of center of Gaussian.
  *  @param fractionSignal Fraction of data due to Gaussian
  *  @param bgLow Low end of flat background range.
  *  @param bgHi High end of flat background range.
  *  @param maxDelay maximum delay in msecs between data events.
**/
public MakeData (Updateable owner,
           Histogram histogram, int numData,
           double fSignalPoint, double fractionSignal,
           double bgLow, double bgHi,
           int maxDelay) {

    fHistogram  = histogram;
    fNumData    = numData;
    fOwner = owner;

    // Background upper and lower range
    fBgLow   = bgLow;
    fBgRange = bgHi - fBgLow;

    // Position of center of Gaussian.
    fSignalPoint    = fSignalPoint;
    fFractionSignal = fractionSignal;

    // Maximum delay time between new data points
    fMaxDelay = maxDelay;
  } // ctor


  /**
    * Simulate data taking by generating both a Gaussian
    * and a flat background. Put in random delays between
    * the data points.
   **/
  public void run () {
    for (int i=0; i < fNumData; i++) {
        double val;

        // Signal events
        if (fRan.nextDouble () < fFractionSignal)
            val = fRan.nextGaussian () + fSignalPoint;
        else
        // Flat background
            val = fBgRange * fRan.nextFloat () + fBgLow;

        // Fill histogram
        fHistogram.add (val);

        // Update the owner. Stop updates if
        // false returned.
        if (!fOwner.update (this) ) break;

        // Pause for random periods between events
        try {
          Thread.sleep (fRan.nextInt (fMaxDelay));
        }
        catch  (InterruptedException e)
        {}
    }

    // Tell the Updateable owner that the fNumData is now filled.
    fOwner.done ();
  } // run

} // class MakeData

/**
  * This interface provides for callbacks to
  * update an object.
 **/
interface Updateable
{
    boolean update ();
    void done ();
}// interface Updateable

PlotPanel.java, PlotFormat.java are unchanged from Chapter 6: Tech : Plotting.

HistPanel.java, Histogram.java are unchanged from Chapter 6: Tech : Histogram Plot

 

javax.swing.Timer

The javax.swing.Timer can also do the above task. Below we show the class SwingTimerHistFillApplet, which is very similar to the above program except for the use of a javax.swing.Timer instance.

SwingTimerHistFill_JApplet11.java - same as TimerHistFill_JApplet11 above except it uses a javax.swing.Timer instance for the histogram updating.

import javax.swing.*;
import java.awt.*;
import java.awt.event.*;

/**
  * This program will run as an applet inside
  * an application frame.
  *
  * The applet uses the HistPanel to display contents of
  * an instance of Histogram. HistFormat used by HistPanel to
  * format the scale values.
  *
  * The javax.swing is used to update the display of the histogram
  * during the filling of the histogram.
  *
  * Includes "Go" button to initiate the filling of the histogram.
  * To simulate data taking, a combination of a Gaussian and random
  *  background values are generated.
  *
  * The number of values taken from entry in a JTextField.
  * The "Clear"  button clears the histogram. In standalone mode,
  * the Exit button closes the program.
  *
 **/
public class SwingTimerHistFillApplet extends JApplet
             implements ActionListener, Updateable
{
  // Use the HistPanel JPanel subclass here
  HistPanel fOutputPanel;

  Histogram fHistogram;
  int fNumDataPoints = 100;

  boolean fMakingHist = false;
  boolean fUpdateDisplay = false;
  MakeData fMakeData;

  // Use the Swing timer
  javax.swing.Timer fTimer;

  // A text field for input strings
  JTextField fTextField;

  // Flag for whether the applet is in a browser
  // or running via the main () below.
  boolean fInBrowser = true;

  //Buttons
  JButton fGoButton;
  JButton fClearButton;
  JButton fExitButton;

  /**
    * Create a User Interface with a histogram and a Go button
    * to initiate processing and a Clear button to clear the .
    * histogram. In application mode, the Exit button stops the
    * program.
   **/
  public void init () {
    Container content_pane = getContentPane ();

    JPanel panel = new JPanel (new BorderLayout ());

    // Use a textfield for an input parameter.
    fTextField =
      new JTextField (Integer.toString (fNumDataPoints), 10);

    // If return hit after entering text, the
    // actionPerformed will be invoked.
    fTextField.addActionListener (this);

    fGoButton = new JButton ("Go");
    fGoButton.addActionListener (this);

    fClearButton = new JButton ("Clear");
    fClearButton.addActionListener (this);

    fExitButton = new JButton ("Exit");
    fExitButton.addActionListener (this);

    JPanel control_panel = new JPanel ();

    control_panel.add (fTextField);
    control_panel.add (fGoButton);
    control_panel.add (fClearButton);
    control_panel.add (fExitButton);

    // Create a histogram and start filling it.
    makeHist ();
    fGoButton.setText ("Stop");
    fClearButton.setEnabled (false);
    if (fInBrowser) fExitButton.setEnabled (false);

    // JPanel subclass here.
    fOutputPanel = new HistPanel (fHistogram);

    panel.add (fOutputPanel,"Center");
    panel.add (control_panel,"South");

    // Add text area with scrolling to the contentPane.
    content_pane.add (panel);

  } // init

  public void actionPerformed (ActionEvent e) {
    Object source = e.getSource ();
    if (source == fGoButton || source == fTextField) {
        String strNumDataPoints = fTextField.getText ();
        try {
          fNumDataPoints = Integer.parseInt (strNumDataPoints);
        }
        catch (NumberFormatException ex) {
          // Could open an error dialog here but just
          // display a message on the browser status line.
          showStatus ("Bad input value");
          return;
        }
        if (!fMakingHist) {
            makeHist ();
            fGoButton.setText ("Stop");
            fClearButton.setEnabled (false);
        } else {
            // Stop button has been pushed
            fGoButton.setText ("Go");
            fClearButton.setEnabled (true);
            fMakingHist = false;
        }

    } else if (source == fTimer) {
        fUpdateDisplay = true;
    } else if (source == fClearButton) {
        fHistogram.clear ();
        repaint ();
    } else if (!fInBrowser)
        System.exit (0);
  } // actionPerformed

  /**
    * Create an instance of Histogram and pass it to the
    * an instance of MakeData. Also, create an instance of
    * javax.swing.Timer, which will invoke actionPerformed ()
    * method above and set the fUpdateDisplay flag every 250ms.
   **/
  void makeHist () {
    if (fMakingHist) return; // only fill one hist at a time.
    fMakingHist = true;

    // Create an instance of the histogram class.
    // Make it wide enough enough to include the data.
    if (fHistogram == null)
        fHistogram = new Histogram ("Gaussian + Random Background",
                                "Data",
                                20,-10.0,10.0);

    // Create the runnable object to fill the histogram
    // Center signal at 3.0 and create background between
    // -10 and 10. The fraction of the data due to the
    // Gaussian will be 0.60. The maximum delay between
    // data poins will be 500msecs.
    fMakeData =
      new MakeData (this, fHistogram, fNumDataPoints,
                    3.0, 0.60, -10.0, 10.0, 500);
    Thread data_thread = new Thread (fMakeData);

    // Before starting the filling, create the timer
    // that will cause the histogram display to update
    // during the filling.

    // Send events very 250ms.
    fTimer = new javax.swing.Timer (250,this);
    // Start after 100ms
    fTimer.setDelay (100);

    // First start the data filling.
    data_thread.start ();

    // Then the timer.
    fTimer.start ();

  } // makeHist

  /**
    *  Repaint the histogram display and turn off the
    *  update display flag. Return the fMakingHist flag
    *  to indicate if updating should continue.
   **/
  public boolean update (Object obj) {
    // Don't update the display until the timer
    // turns on the fUpdateDisplay flag.
    if (fUpdateDisplay) {
      // Possible this method called before fOutputPanel
      // created in the init (). So check if it exists
      // before attempting to repaint.
      if (fOutputPanel != null) fOutputPanel.repaint ();
      fUpdateDisplay = false;
    }
    return fMakingHist;
  } //update


  /**  Called when the histogram filling is finished. **/
  public void done () {
    fMakingHist = false;

    // Stop the histogram display updates.
    fTimer.stop ();

    // Update one last time
    fOutputPanel.repaint ();

    // Reset the buttons.
    fGoButton.setText ("Go");
    fClearButton.setEnabled (true);

  } // done

  public static void main (String[] args) {
    //
    int frame_width=450;
    int frame_height=300;

    //
    SwingTimerHistFillApplet applet = new SwingTimerHistFillApplet ();
    applet.fInBrowser = false;
    applet.init ();

    // Following anonymous class used to close window & exit program
    JFrame f = new JFrame ("Demo");
    f.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);

    // Add applet to the frame
    f.getContentPane ().add ( applet);
    f.setSize (new Dimension (frame_width,frame_height));
    f.setVisible (true);
  } // main

} // class SwingTimerHistFillApplet

 

Note that since the two timer classes have the same name, so you will need to specify the correct package for the one you choose.

See the following references for more about these timer classes and the options they provide.

References & Web Resources

Latest update: Nov. 8, 2004

              Tech
Timers
  Demo 1
Hist. Adapt Range
  Demo 2
Sorting in Java
  Demo 3
Histogram Median
  Demo 4
Refactoring
  Demo 5
Error Bars
  Demo 6
Exercises

           Physics
Least Squares Fit
  Demo 1
Fit to Polynomial
  Demo 2
Fit Hist Errors
  Demo 3
Discretization
  Demo 4
Timing
  Demo 5
Exercises

  Part I Part II Part III
Java Core 1  2  3  4  5  6  7  8  9  10  11  12 13 14 15 16 17
18 19 20
21
22 23 24
Supplements

1  2  3  4  5  6  7  8  9  10  11  12

Tech 1  2  3  4  5  6  7  8  9  10  11  12
Physics 1  2  3  4  5  6  7  8  9  10  11  12

Java is a trademark of Sun Microsystems, Inc.