Worker and Timer Threads
An Introduction with Examples in Java |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
SwingWorker
SwingWorker
execute()
method
doInBackground()
is called
PropertyChangeEvent
s
done()
is called
SwingWorker
:
setProgress()
to generate
PropertyChangeEvent
objectspublish()
to cause the process()
method to be calledPropertyChangeListener
:
propertyChange()
messages
and check the name (e.g. "progress", "state")SwingWorker
:
isCancelled()
SwingWorker
:
cancel()
method to change its stateSwingWorker
Threadsexecute()
is called in.doInBackground()
is
called in.process()
and done()
are called in (and the thread that is used to
inform PropertyChangeListener
objects).
import java.util.*; import javax.swing.*; /** * Compute pi using the van Wijngaarden transformation * of the Gregory-Leibniz series * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class PiCalculator extends SwingWorker<Double,Double> { // SwingWorker<T,V> where: // T is the result type returned by doInBackground() and get() // V is the type used for intermediate results by publish() and process() // private int digits; /** * Explicit Value Constructor * * @param digits The number of digits of accuracy */ public PiCalculator(int digits) { this.digits = digits; } /** * Calculate the i,j term in the transformation */ private double pi(int i, int j) { if (isCancelled()) return 4.0; double den, num, result; if (j == 0) return 0.0; // We could call setProgress() and pass an int in [0,100] // to cause PropertyChangeEvent objects to be fired // to listeners (in the Event Dispatch Thread). // // We could also call publish() to cause this object's // process() method to be called in the Event DispatchThread. if (i == 0) { // pi(0,1): 4/1 pi(0,2): 4/1 - 4/3 pi(0,3): 4/1-4/3+4/5 ... result = 0.0; for (int k=1; k<=j; k++) { num = Math.pow(-1.0, k+1) * 4.0; den = (k * 2.0) - 1; result += num / den; } } else { result = (pi(i-1,j) + pi(i-1,j+1))/2.0; } return result; } /** * The code to execute in the background thread */ public Double doInBackground() { double result; result = pi(digits+1, digits+1); // This will cause the result to be retrievable // using the get() method return Double.valueOf(result); } }
import java.awt.*; import java.awt.event.*; import java.beans.*; import javax.swing.*; /** * An example that uses PiCalculator (which is a the SwingWorker) * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class PiDriver implements ActionListener, PropertyChangeListener { private int digits; private JButton cancelButton, startButton; private JTextField digitsField, resultArea; private PiCalculator task; /** * The entry point */ public static void main(String[] args) { new PiDriver(); } /** * Default Constructor */ public PiDriver() { Container contentPane; JFrame frame; JPanel north; frame = new JFrame(); frame.setTitle("Calculate pi"); contentPane = frame.getContentPane(); contentPane.setLayout(new BorderLayout()); resultArea = new JTextField(); contentPane.add(resultArea, BorderLayout.SOUTH); north = new JPanel(); north.add(new JLabel("Digits:")); digitsField = new JTextField(10); north.add(digitsField); startButton = new JButton("Start"); startButton.addActionListener(this); startButton.setEnabled(true); north.add(startButton); cancelButton = new JButton("Cancel"); cancelButton.addActionListener(this); cancelButton.setEnabled(false); north.add(cancelButton); contentPane.add(north, BorderLayout.NORTH); frame.setSize(400,100); frame.setVisible(true); task = null; } /** * Handle actionPerformed() messages (required by * ActionListener) * * @param evt The ActionEvent that generated the message */ public void actionPerformed(ActionEvent evt) { String ac; ac = evt.getActionCommand(); if (ac.equals("Start") && (task == null)) { try { resultArea.setText(""); digits = Integer.parseInt(digitsField.getText()); task = new PiCalculator(digits); task.addPropertyChangeListener(this); startButton.setEnabled(false); cancelButton.setEnabled(true); task.execute(); } catch (NumberFormatException nfe) { nfe.printStackTrace(); } } else if (ac.equals("Cancel")) { if (task != null) { startButton.setEnabled(true); cancelButton.setEnabled(false); task.cancel(true); task = null; } } } /** * Handle propertyChange() messages (required by * PropertyChangeListener) * * @param evt The PropertyChangeEvent that generated the message */ public void propertyChange(PropertyChangeEvent evt) { String format; if (evt.getPropertyName().equals("progress")) { // Could do something like: // progressBar.setValue((Integer)evt.getNewValue()); } else if (evt.getPropertyName().equals("state")) { if (evt.getNewValue().equals(SwingWorker.StateValue.DONE)) { startButton.setEnabled(true); cancelButton.setEnabled(false); format = "%"+(digits+1)+"."+digits+"f\n"; try { resultArea.setText(String.format(format, task.get())); } catch (Exception e) { } task = null; } } } }
javax.swing.Timer
addActionListener()
(either explicitly
or using the constructor)start()
or restart()
ActionListener
BlinkingLabel
package gui; import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * A Jlabel that blinks (in an annoying way) * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class BlinkingLabel extends JLabel implements ActionListener { private boolean status; private Color textColor; private Timer timer; /** * Explicit Value Constructor */ public BlinkingLabel(String text) { super(text); performCommonConstructionTasks(); } /** * Explicit Value Constructor */ public BlinkingLabel(String text, Icon icon, int horizontalAlignment) { super(text, icon, horizontalAlignment); performCommonConstructionTasks(); } /** * Explicit Value Constructor */ public BlinkingLabel(String text, int horizontalAlignment) { super(text, horizontalAlignment); performCommonConstructionTasks(); } /** * Handle actionPerformed() message (required by ActionListener) * * @param evt The ActionEvent that generated the message */ public void actionPerformed(ActionEvent evt) { if (status) setForeground(textColor); else setForeground(Color.RED); status = !status; } /** * Perform common construction tasks */ private void performCommonConstructionTasks() { timer = new Timer(500, this); status = false; } /** * Start the blinking */ public void start() { textColor = getForeground(); timer.restart(); } /** * Stop the blinking */ public void stop() { timer.stop(); setForeground(textColor); } }