Start of Tutorial > Start of Trail > Start of Lesson |
Search
Feedback Form |
This section assumes that you are familiar with threads lightweight processes that can run concurrently. If you are unfamiliar with threads, you can read Threads: Doing Two or More Tasks At Once.This section discusses two aspects of thread usage. We first discuss threads as it applies to every Swing application namely, why all code that deals with components needs to execute on a specific thread. We also discuss the
invokeLater
andinvokeAndWait
methods, which are used to ensure that code executes on this special thread. The rest of the section applies only to those who want to use additional threads to improve performance in their application. We also include a discussion ofSwingWorker
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 allComponent
s. Specifically, AWT components are not guaranteed to be thread safe.
Swing event-handling and painting code executes in a single thread, called the event-dispatching thread. This ensures that each event handler finishes 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 created, modified, and queried only from the event-dispatching thread.
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. [PENDING: The following red text belongs in a footnote.] Realized means that the component has been painted onscreen, or is ready to be painted. The methodssetVisible(true)
andpack
cause a window to be realized, which in turn causes the components it contains to be realized. While this worked for most applications, in certain situations it could cause problems. Out of all the demos in the Swing Tutorial, we encountered a problem only in ComponentEventDemo. In that case, sometimes when you launched the demo, it would not come up because it would deadlock when updating the text area if the text area had not yet been realized, while other times it would come up without incident.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.You have probably noticed that most of the tutorial's demos use a standardized
main
method that calls theSwingUtilities
methodinvokeLater
to ensure that the GUI is created on the event-dispatching thread. Here is an example of themain
method from the FocusConceptsDemo example. We have also included the source forcreateAndShowGUI
a private, static method called by eachmain
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(); } }); }
You can call
invokeLater
from any thread to request the event-dispatching thread to run certain code. You must put this code in therun
method of aRunnable
object and specify theRunnable
object as the argument toinvokeLater
. TheinvokeLater
method returns immediately, without waiting for the event-dispatching thread to execute the code. Here's an example of usinginvokeLater
:Runnable updateAComponent = new Runnable() { public void run() { component.doSomething(); } }; SwingUtilities.invokeLater(updateAComponent);
The
invokeAndWait
method is just likeinvokeLater
, except thatinvokeAndWait
doesn't return until the event-dispatching thread has executed the specified code. Whenever possible, you should useinvokeLater
instead ofinvokeAndWait
it is very easy to cause a deadlock usinginvokeAndWait
. If you useinvokeAndWait
, make sure that the thread that callsinvokeAndWait
does not 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]); }
When properly used, 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 threads 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
or one of theTimer
classes. ASwingWorker
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 Threads: Doing Two or More Tasks At Once.You can use several techniques to make multi-threaded Swing programs work well:
For information and examples of using timers, see How to Use Swing Timers.
- 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) orinvokeAndWait
.
- 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, you can use the
SwingUtilities.isEventDispatchThread()
method, which returns true if your code is executing in the event-dispatching thread. You can safely callinvokeLater
from any thread, butinvokeAndWait
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 to do so.
- If you need to update a component at a regular interval, use a timer.
For more information about Swing thread issues, see the article index in The Swing Connection.The
Note: The implementation of theSwingWorker
class has been updated twice, most recently in February 2000. The first update (in January 1999) allowed programs to safely interrupt the worker thread. The most recent update (called "SwingWorker 3") was to fix a subtle threading bug that could cause aNullPointerException
.SwingWorker
class is implemented inSwingWorker.java
, which is not in the Swing release. To use theSwingWorker
class, you first create a subclass of it. The subclass must implement theconstruct
method so that it contains the code to perform your lengthy operation. When you instantiate yourSwingWorker
subclass, theSwingWorker
creates a thread but does not (as of SwingWorker 3) start it. You then invokestart
on yourSwingWorker
object to start the thread, which then calls yourconstruct
method.Here is 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. If you need to get the value, you can do so by invoking theget
method on yourSwingWorker
object. Be careful about usingget
. Because it blocks, it can cause deadlock. If necessary, you can interrupt the thread (causingget
to return) by invokinginterrupt
on theSwingWorker
.If you need to update the GUI when the time-consuming operation completes, you can do so either by using
get
(which is dangerous, as we noted) or by overriding thefinished
method in yourSwingWorker
subclass. Thefinished
method runs after theconstruct
method returns. Because thefinished
method 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 yourfinished
implementation.The following example of implementing
finished
is taken fromIconDemoApplet.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.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 How to Monitor Progress. Also,TumbleItem.java
, which is discussed in How to Make Applets, uses both aSwingWorker
and aTimer
.
Start of Tutorial > Start of Trail > Start of Lesson |
Search
Feedback Form |
Copyright 1995-2004 Sun Microsystems, Inc. All rights reserved.