Using the JFC/Swing Packages |
Note: This section is a simplified version of an article in The Swing Connection. For more information about Swing thread issues, see the article: Threads and Swing.
This section tells you how to use the Swing API in a thread-safe way. If your program is an applet, it's generally safe to construct its GUI in the
init
method. If your program is an application with the following common pattern, then you're safe:However, if your program creates threads to perform tasks that affect the GUI, or it manipulates the already-visible GUI in response to anything but an AWT event, then read on! Four subsections follow; please read at least the first two.//Thread-safe example public class MyApplication { public static void main(String[] args) { JFrame f = new JFrame(...); ...//Add components to the frame here... f.pack(); f.setVisible(true); //Don't do any more GUI work here. } ... //All manipulation of the GUI -- setText, getText, etc. -- //is performed in event handlers such as actionPerformed(). ... }
- The Single-Thread Rule
- Swing components can be accessed by only one thread at a time. Generally, this thread is the event-dispatching thread.
- Exceptions to the Rule
- A few operations are guaranteed to be thread-safe.
- How to Execute Code in the Event-Dispatching Thread
- If you need access to the UI from outside event-handling or drawing code, then you can use the
SwingUtilities
invokeLater
orinvokeAndWait
method.- How to Create Threads
- If you need to create a thread -- for example, to handle a job that's computationally expensive or I/O bound -- you can use a thread utility class such as
SwingWorker
orTimer
.The single-thread rule is as follows:
Rule: Once a Swing component has been realized, all code that might affect or depend on the state of that component should be executed in the event-dispatching thread.
This rule might sound scary, but for many simple programs, you don't have to worry about threads. Before we go into detail about how to write Swing code, let's define two terms: realized and event-dispatching thread.
Realized means that the component's
paint
method has been or might be called. A Swing component that's a top-level window is realized by having one of these methods invoked on it:setVisible(true)
,show
, or (this might surprise you)pack
. Once a window is realized, all components that it contains are realized. Another way to realize a component is to add it to a container that's already realized. You'll see examples of realizing components later.The event-dispatching thread is the thread that executes drawing and event-handling code. For example, the
paint
andactionPerformed
methods are automatically executed in the event-dispatching thread. Another way to execute code in the event-dispatching thread is to use the SwingUtilitiesinvokeLater
method.Exceptions to the Rule
There are a few exceptions to the rule that all code that might affect a realized Swing component must run in the event-dispatching thread:
- A few methods are thread-safe.
- In the Swing API documentation, thread-safe methods are marked with this text:
This method is thread safe, although most Swing methods are not. Please see Threads and Swing for more information.- An application's GUI can often be constructed and shown in the main thread.
- As long as no components (Swing or otherwise) have been realized in the current runtime environment, it's fine to construct and show a GUI in the main thread of an application. To help you see why, here's an analysis of the thread safety of the thread-safe example. To refresh your memory, here are the important lines from the example:
public static void main(String[] args) { JFrame f = new JFrame(...); ...//Add components to the frame here... f.pack(); f.setVisible(true); //Don't do any more GUI work here. }
- The example constructs the GUI in the main thread. In general, you can construct (but not show) a GUI in any thread, as long as you don't make any calls that refer to or affect already-realized components.
- The components in the GUI are realized by the
pack
call.- Immediately afterward, the components in the GUI are shown with the
setVisible
(orshow
) call. Technically, thesetVisible
call is unsafe because the components have already been realized by thepack
call. However, because the program doesn't already have a visible GUI, it's exceedingly unlikely that apaint
call will occur beforesetVisible
returns.- The main thread executes no GUI code after the
setVisible
call. This means that all GUI work moves from the main thread to the event-dispatching thread, and the example is, in practice, thread-safe.
- An applet's GUI can be constructed and shown in the
init
method:- Existing browsers don't draw an applet until after its
init
andstart
methods have been called. Thus, constructing the GUI in the applet'sinit
method is safe, as long as you never callshow()
orsetVisible(true)
on the actual applet object.By the way, applets that use Swing components must be implemented as subclasses of
JApplet
, and components should be added to theJApplet
content pane, rather than directly to theJApplet
. As for any applet, you should never perform time-consuming initialization in theinit
orstart
method; instead, you should start a thread that performs the time-consuming task. [PENDING: move this paragraph to the applet page, leaving an x-ref here.]
- The following
JComponent
methods are safe to call from any thread:repaint
,revalidate
, andinvalidate
.- The
repaint
andrevalidate
methods enqueue requests for the event-dispatching thread to callpaint
andvalidate
, respectively. Theinvalidate
method just marks a component and all of its direct ancestors as requiring validation.
- Listener lists can be modified from any thread.
- It's always safe to call the
addListenerTypeListener
andremoveListenerTypeListener
methods. The add/remove operations have no effect on an event dispatch that's under way.How to Execute Code in the Event-Dispatching Thread
Most post-initialization GUI work naturally occurs in the event-dispatching thread. Once the GUI is visible, most programs are driven by events such as button actions or mouse clicks, which are always handled in the event-dispatching thread.However, some programs need to perform non-event-driven GUI work after the GUI is visible. Here are some examples:
- Programs that must perform a lengthy initialization operation before they can be used:
- This kind of program should generally show some GUI while the initialization is occurring, and then update or change the GUI. The initialization should not occur in the event-dispatching thread; otherwise, repainting and event dispatch would stop. However, after initialization the GUI update/change should occur in the event-dispatching thread, for thread-safety reasons.
- Programs whose GUI must be updated as the result of non-AWT events:
- For example, suppose a server program can get requests from other programs that might be running on different machines. These requests can come at any time, and they result in one of the server's methods being invoked in some possibly unknown thread. How can that method update the GUI? By executing the GUI update code in the event-dispatching thread.
The
SwingUtilities
class provides two methods to help you run code in the event-dispatching thread:
invokeLater
: Requests that some code be executed in the event-dispatching thread. This method returns immediately, without waiting for the code to execute.
invokeAndWait
: Acts like
invokeLater
, except that this method waits for the code to execute. As a rule, you should useinvokeLater
instead of this method.This page gives you some examples of using this API. Also see the BINGO example, especially the following classes:
CardWindow
,ControlPane
,Player
, andOverallStatusPane
.
Using the
invokeLater
MethodYou 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 doWorkRunnable = new Runnable() { public void run() { doWork(); } }; SwingUtilities.invokeLater(doWorkRunnable);Using the
invokeAndWait
MethodThe
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
. 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]); }How to Create Threads
If you can get away with it, avoid using threads. Threads can be difficult to use, and they make programs harder to debug. In general, they just aren't necessary for strictly GUI work, such as updating component properties.
However, sometimes threads are necessary. Here are some typical situations where threads are used:
- To perform a time-consuming task without locking up the event-dispatching thread. Examples include making extensive calculations, doing something that results in many classes being loaded (initialization, for example), and blocking for network or disk I/O.
- To perform an operation repeatedly, usually with some predetermined period of time between operations.
- To wait for messages from clients.
You can use two classes to help you implement threads:
SwingWorker
: Creates a background thread to execute time-consuming operations.
Timer
: Creates a thread that executes some code one or more times, with a user-specified delay between executions. For detailed information about timers, see How to Use Timers.Using the
SwingWorker
ClassTheSwingWorker
class is implemented inSwingWorker.java
, which is not in the Swing release.SwingWorker
does all the dirty work of implementing a background thread. Although many programs don't need background threads, background threads are sometimes useful for performing time-consuming operations, which can improve the perceived performance of a program.To use the
SwingWorker
class, you first create a subclass of it. In the subclass, you must implement theconstruct
method so that it contains the code to perform your lengthy operation. When you instantiate yourSwingWorker
subclass, theSwingWorker
creates a thread that calls yourconstruct
method. When you need the object returned by theconstruct
method, you call theSwingWorker
'sget
method. Here's an example of usingSwingWorker
:...//in the main method: final SwingWorker worker = new SwingWorker() { public Object construct() { return new ExpensiveDialogComponent(); } }; ...//in an action event handler: JOptionPane.showMessageDialog (f, worker.get());When the program's
main
method creates theSwingWorker
object, theSwingWorker
immediately starts a new thread that instantiatesExpensiveDialogComponent
. Themain
method also constructs a GUI that consists of a window with a button.When the user clicks the button, the program blocks, if necessary, until the
ExpensiveDialogComponent
has been created. The program then shows a modal dialog containing theExpensiveDialogComponent
. You can find the entire program inPasswordDemo.java
. Also, the example program provided in How to Use Progress Bars runs a long task in aSwingWorker
thread.
Using the JFC/Swing Packages |