How to Use Threads

 <  Day Day Up  >  

The information in this section assumes that you are familiar with threads ”lightweight processes that can run concurrently. If you are unfamiliar with threads, you can read The Java Tutorial trail, "Threads: Doing Two or More Tasks at Once." [75]

[75] This trail is on the CD and online at: http://java.sun.com/docs/books/tutorial/essential/threads/index.html.

This section discusses two aspects of thread usage. We first discuss threads as they apply to every Swing application ”namely, why all code that paints or handles events needs to execute on a specific thread. We also discuss the invokeLater and invokeAndWait methods , which are used to ensure that events are handled on this special thread. The rest of this section applies only to those who want to use additional threads to improve performance in their application. Also included is a discussion of SwingWorker ”a handy class used by many of our demos to offload time-consuming tasks.

Note: Although this section talks about Swing, the same issues apply to all Components . Specifically, AWT components are not guaranteed to be thread safe.


The Event-Dispatching Thread

Swing event-handling and painting code executes in a single thread, which is called the event-dispatching thread . This ensures that each event handler will finish executing before the next one executes and that painting isn't interrupted by events. To avoid the possibility of deadlock, you must take extreme care that Swing components and models are modified or queried only from the event-dispatching thread. As long as your program creates its GUI from the event-dispatching thread and modifies the GUI only from event handlers, it is thread safe.

You have probably noticed that most of this book's demos use a standardized main method that calls the SwingUtilities method invokeLater to ensure that the GUI is created on the event-dispatching thread. Here's an example of the main method from the FocusConceptsDemo example. We have also included the source for createAndShowGUI ”a private, static method called by each main method where the creation of the GUI is handled.

 /**  * Create the GUI and show it.  For thread safety,  * this method should be invoked from the  * event-dispatching thread.  */ private static void createAndShowGUI() {     //Make sure we have nice window decorations.     JFrame.setDefaultLookAndFeelDecorated(true);     //Create and set up the window.     frame = new JFrame("FocusConceptsDemo");     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);     //Create and set up the content pane.     JComponent newContentPane = new FocusConceptsDemo();     newContentPane.setOpaque(true); //content panes must be opaque     frame.setContentPane(newContentPane);     //Display the window.     frame.pack();     frame.setVisible(true); } public static void main(String[] args) {     //Schedule a job for the event-dispatching thread:     //creating and showing this application's GUI.     javax.swing.SwingUtilities.invokeLater(new Runnable() {         public void run() {             createAndShowGUI();         }     }); } 

Note: We used to say that you could create the GUI on the main thread as long as you didn't modify components that had already been realized. [76] While this worked for most applications, in certain situations it could cause problems. Out of all the demos in this book, we encountered a problem only in ComponentEventDemo . In that case, sometimes when the demo was launched, it would not come up because it would deadlock when updating the text area if the text area had not yet been realized; at other times it would come up without incident.

[76] "Realized" means that the component has been painted onscreen, or is ready to be painted . The methods setVisible(true) and pack cause a window to be realized, which in turn causes the components it contains to be realized.

To avoid the possibility of thread problems, we recommend that you use invokeLater to create the GUI on the event-dispatching thread for all new applications. If you have old programs that are working fine, they are probably OK; however, you might want to convert them when it's convenient to do so.


Using the invokeLater Method

You can call invokeLater from any thread to request the event-dispatching thread to run certain code. This code must be in the run method of a Runnable object and should specify the Runnable object as invokeLater 's argument. The invokeLater method returns immediately without waiting for the event-dispatching thread to execute the code.

Here's an example of using invokeLater :

 Runnable updateAComponent = new Runnable() {     public void run() { component.doSomething(); } }; SwingUtilities.invokeLater(updateAComponent); 

Using the invokeAndWait Method

The invokeAndWait method is just like invokeLater , except that it doesn't return until the event-dispatching thread has executed the specified code. Whenever possible, use invokeLater instead of invokeAndWait , which can cause a deadlock. If you do use invokeAndWait , make sure the thread that calls it doesn't hold any locks that other threads might need while the call is occurring.

Here's an example of using invokeAndWait :

 void showHelloThereDialog() throws Exception {     Runnable showModalDialog = new Runnable() {         public void run() {             JOptionPane.showMessageDialog(myMainFrame,                                           "Hello There");         }     };     SwingUtilities.invokeAndWait(showModalDialog); } 

Similarly, a thread that needs access to GUI state, such as the contents of a pair of text fields, might have the following code:

 void printTextField() throws Exception {     final String[] myStrings = new String[2];     Runnable getTextFieldText = new Runnable() {         public void run() {             myStrings[0] = textField0.getText();             myStrings[1] = textField1.getText();         }     };     SwingUtilities.invokeAndWait(getTextFieldText);     System.out.println(myStrings[0] + " " + myStrings[1]); } 

Using Threads to Improve Performance

When used properly, threads can be a powerful tool. However, you must proceed with caution when using threads in a Swing program. Despite the dangers, threads can be invaluable. You can use them to improve your program's perceived performance, and sometimes they can simplify a program's code or architecture. Here are some typical situations where threads are used:

  • To move a time-consuming initialization task out of the main thread so that the GUI comes up faster. Examples of time-consuming tasks include making extensive calculations and blocking for network or disk I/O (loading images, for example).

  • To move a time-consuming task out of the event-dispatching thread so that the GUI remains responsive .

  • To perform an operation repeatedly, usually with some predetermined period of time between operations.

  • To wait for messages from other programs.

If you need to create a thread, you can avoid some common pitfalls by implementing the thread with a utility class, such as SwingWorker [77] or one of the Timer [78] classes. A SwingWorker object creates a thread to execute a time-consuming operation. After the operation is finished, SwingWorker gives you the option of executing some additional code in the event-dispatching thread. Timers are useful for performing a task either repeatedly or after a specified delay. If you need to implement your own threads, you can find information on doing so in The Java Tutorial trail, "Threads: Doing Two or More Tasks at Once." [79]

[77] You can find the source code for SwingWorker.java online and on the CD: JavaTutorial/uiswing/misc/example-1dot4/SwingWorker.java .

[78] Timers are discussed in the next section How to Use Timers (page 639).

[79] This trail is available online and on the CD: JavaTutorial/essential/threads/index.html .

You can use several techniques to make multi-threaded Swing programs work well:

  • If you need to update a component but your code isn't executing in an event listener, use one of the two SwingUtilities methods: invokeLater (preferred) or invoke-AndWait .

  • If you aren't sure whether your code is executing in an event listener, then you should analyze your program's code and document which thread each method is (and can be) called from. Failing that, use the SwingUtilities.isEventDispatchThread() method, which returns true if your code is executing in the event-dispatching thread. You can safely call invokeLater from any thread, but invokeAndWait throws an exception if it's called from the event-dispatching thread.

  • If you need to update a component after a delay (whether or not your code is currently executing in an event listener), use a timer.

  • If you need to update a component at a regular interval, use a timer.

For information and examples of using timers, see How to Use Timers (page 639).

Using the SwingWorker Class

The SwingWorker class is implemented in SwingWorker.java , [80] which is not in the Swing release. To use SwingWorker , first create a subclass of it. The subclass must implement the construct method so that it contains the code to perform your lengthy operation. When you instantiate your SwingWorker subclass, the SwingWorker object creates a thread but (as of SwingWorker 3 ) doesn't start it. You then invoke start on your SwingWorker object to start the thread, which calls your construct method.

[80] You can find the source code for SwingWorker.java online and on the CD: JavaTutorial/uiswing/misc/example-1dot4/SwingWorker.java .

Note: The implementation of the SwingWorker class has been updated twice, most recently in February 2000. We recommend that you occasionally check online to see whether a new version of SwingWorker is available at: http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html#SwingWorker.


Here's an example of using a SwingWorker to move a time-consuming task from an action event listener into a background thread so that the GUI remains responsive.

 //OLD CODE: public void actionPerformed(ActionEvent e) {     ...  //...code that might take a while to execute is here...  ... } //BETTER CODE: public void actionPerformed(ActionEvent e) {     ...     final SwingWorker worker = new SwingWorker() {         public Object construct() {  //...code that might take a while to execute is here...  return someValue;         }     };  worker.start();  //required for SwingWorker 3  ... } 

The value that construct returns can be any object. You get the value by invoking the get method on your SwingWorker object. Be careful with get ; because it blocks, it can cause deadlock. If necessary, you can interrupt the thread ( causing get to return) by invoking interrupt on the SwingWorker .

If you need to update the GUI when the time-consuming operation completes, you can either use get (which is dangerous, as we noted) or override the finished method in your SwingWorker subclass. The finished method runs after the construct method returns. Because finished executes in the event-dispatching thread, you can safely use it to update Swing components. Of course, you shouldn't put time-consuming operations in your finished implementation.

The following example of implementing finished is taken from IconDemoApplet.java . For a full discussion of this applet, including how it improves perceived performance by using background threads to load images, see How to Use Icons (page 603).

 public void actionPerformed(ActionEvent e) {     ...     if (icon == null) {     //haven't viewed this photo before         loadImage(imagedir + pic.filename, current);     } else {         updatePhotograph(current, pic);     } } ... //Load an image in a separate thread. private void loadImage(final String imagePath, final int index) {     final SwingWorker worker = new SwingWorker() {         ImageIcon icon = null;         public Object construct() {             icon = new ImageIcon(getURL(imagePath));             return icon; //return value not used by this program         }         //Runs on the event-dispatching thread.         public void finished() {             Photo pic = (Photo)pictures.elementAt(index);             pic.setIcon(icon);             if (index == current)                 updatePhotograph(index, pic);         }     };     worker.start(); } 

For more examples of using SwingWorker , go to Using Progress Monitors (page 305). TumbleItem , which is discussed in How to Make Applets (page 149) in Chapter 7, uses both a SwingWorker and a Timer .

For more information on Swing thread issues, see the article index in The Swing Connection at: http://java.sun.com/products/jfc/tsc/articles/index.html.

 <  Day Day Up  >  


JFC Swing Tutorial, The. A Guide to Constructing GUIs
The JFC Swing Tutorial: A Guide to Constructing GUIs (2nd Edition)
ISBN: 0201914670
EAN: 2147483647
Year: 2004
Pages: 171

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