Why Isnt the Swing Toolkit Multithread-Safe?

Chapter 9 - Threads and Swing

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

Displaying Elapsed Time on a JLabel
DigitalTimer (see Listing 9.10) extends JLabel and uses an internal thread to update the text on the label with the elapsed time since the component was constructed . In this class it is important to use SwingUtilities.invokeAndWait() to have the event thread actually update the text on the label.
Listing 9.10  DigitalTimer.javaExtending JLabel to Continually Display Elapsed Time
  1: import java.awt.*;
  2: import java.text.*;
  3: import java.lang.reflect.*;
  4: import javax.swing.*;
  5:
  6: public class DigitalTimer extends JLabel {
  7:     private volatile String timeText;
  8:
  9:     private Thread internalThread;
10:     private volatile boolean noStopRequested;
11:
12:     public DigitalTimer() {
13:         setBorder(BorderFactory.createLineBorder( Color .black));
14:         setHorizontalAlignment(SwingConstants.RIGHT);
15:         setFont(new Font(SansSerif, Font.BOLD, 16));
16:         setText(00000.0); // use to size component
17:         setMinimumSize(getPreferredSize());
18:         setPreferredSize(getPreferredSize());
19:         setSize(getPreferredSize());
20:
21:         timeText = 0.0;
22:         setText(timeText);
23:
24:         noStopRequested = true;
25:         Runnable r = new Runnable() {
26:                 public void run() {
27:                     try {
28:                         runWork();
29:                     } catch (Exception x) {
30:                         x.printStackTrace();
31:                     }
32:                 }
33:             };
34:
35:         internalThread = new Thread(r, DigitalTimer);
36:         internalThread.start();
37:     }
38:
39:     private void runWork() {
40:         long startTime = System.currentTimeMillis();
41:         int tenths = 0;
42:         long normalSleepTime = 100;
43:         long nextSleepTime = 100;
44:         DecimalFormat fmt = new DecimalFormat(0.0);
45:
46:         Runnable updateText = new Runnable() {
47:                 public void run() {
48:                     setText(timeText);
49:                 }
50:             };
51:
52:         while (noStopRequested) {
53:             try {
54:                 Thread.sleep(nextSleepTime);
55:
56:                 tenths++;
57:                 long currTime = System.currentTimeMillis();
58:                 long elapsedTime = currTime - startTime;
59:
60:                 nextSleepTime = normalSleepTime +
61:                     ((tenths * 100) - elapsedTime);
62:
63:                 if (nextSleepTime < 0) {
64:                     nextSleepTime = 0;
65:                 }
66:
67:                 timeText = fmt.format(elapsedTime / 1000.0);
68:                 SwingUtilities.invokeAndWait(updateText);
69:             } catch (InterruptedException ix) {
70:                 // stop running
71:                 return;
72:             } catch (InvocationTargetException x) {
73:                 // If an exception was thrown inside the
74:                 // run() method of the updateText Runnable.
75:                 x.printStackTrace();
76:             }
77:         }
78:     }
79:
80:     public void stopRequest() {
81:         noStopRequested = false;
82:         internalThread.interrupt();
83:     }
84:
85:     public boolean isAlive() {
86:         return internalThread.isAlive();
87:     }
88:
89:     public static void main(String[] args) {
90:         DigitalTimer dt = new DigitalTimer();
91:
92:         JPanel p = new JPanel(new FlowLayout());
93:         p.add(dt);
94:
95:         JFrame f = new JFrame(DigitalTimer Demo);
96:         f.setContentPane(p);
97:         f.setSize(250, 100);
98:         f.setVisible(true);
99:     }
100: }
In main() (lines 8999), a new DigitalTimer instance is constructed (line 90) and put into a JPanel with a FlowLayout to let it take on its preferred size (lines 9293). This JPanel is put into a JFrame and set visible.
Inside the constructor (lines 1237), the border, alignment, and font for the label are set. A sample text string of 00000.0 is used to initially size the component (lines 1619). timeText is initialized to be 0.0 . timeText is declared to be volatile (line 7) because (after construction) it is set by the internal thread and read by the event thread. The rest of the constructor gets the internal thread up and running.
The internal thread invokes runWork() (lines 3978) to keep track of time and update the label. Much of the work inside this method is done to keep the elapsed time as accurate as possible. (See Chapter 4, Implementing Runnable Versus Extending Thread, for a more in-depth discussion of the accuracy issues and techniques used.) The Runnable instance referred to by updateText (lines 4650) is used by SwingUtilities.invokeAndWait() to get the event thread to update the text on the label. Notice that the same Runnable instance is used over and over inside the while loop. The Runnable reads the volatile member variable timeText to find out what text should be displayed.
In the while loop (lines 5277), the internal thread sleeps for a while (about 0.1 seconds), increments the tenths counter, and calculates the elapsed time (lines 5458). The nextSleepTime is calculated to keep the clock from running too fast or too slow (lines 6065). The elapsed time is converted into seconds (from milliseconds ) and formatted into a String that is stored in timeText (line 67). Next , SwingUtilities.invokeAndWait() is used to get the event thread to update the text currently displayed on the label (line 68). SwingUtilities.invokeAndWait() was used instead of SwingUtilities.invokeLater() so that the internal thread would not get ahead of the event thread.
Figure 9.13 shows how DigitalTimer appears after 15.5 seconds have elapsed.
Figure 9.13: DigitalTimer after 15.5 seconds have elapsed.

Toc


Java Thread Programming
Java Thread Programming
ISBN: 0672315858
EAN: 2147483647
Year: 2005
Pages: 149
Authors: Paul Hyde

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net