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
|