An important compromise was made early in the design of Swing relating to speed, GUI consistency, and thread safety. To provide maximum performance and simplicity in the common case, Swing does not explicitly synchronize access to most Swing component methods. This means that most Swing components are, technically, not threadsafe for multithreaded applications. Now don’t panic: it’s not as bad as it sounds because there is a plan. All event processing in AWT/Swing is handled by a single system thread using a single system event queue. The queue serves two purposes. First, it eliminates thread safety issues by making all GUI modifications happen in a single thread. Second, the queue imposes a strict ordering of all activity in Swing. Because painting is handled in Swing using events, all screen updating is also ordered with respect to all event handling.
What this means for you is that multithreaded programs need to be careful about how they update Swing components after they are realized (added to a visible container). If you make arbitrary modifications to Swing components from your own threads, you run the risk of malformed rendering on the screen and inconsistent behavior.
There are several conditions under which it is always safe to modify
a Swing component. First, Swing components can be modified before they are
realized. The term realized originates from the days
when the component would have created its peer object. It is the point
when it is added to a visible container or when it is made visible in the
case of a window. Most of our examples in this book set up GUI components
in their main()
method, add them to a
JFrame
, and then, as their final
action, cause the JFrame
to be
displayed using setVisible()
. This
setup style is safe because components are not realized until the
container is made visible. Actually, that last sentence is not entirely
true. Technically, components can also be realized by the JFrame() pack()
method. However, because no GUI
is shown until the container is made visible, it is unlikely that any GUI
activity can be mishandled.
Second, it’s safe to modify Swing components from code that is
already running from the system event handler’s thread. Because all events
are processed by the event queue, the methods of all Swing event listeners
are normally invoked by the system event-handling thread. This means that
event handler methods and, transitively, any methods called from those
methods during the lifetime of that call, can freely modify Swing GUI
components because they are already running in the system
event-dispatching thread. If unsure of whether some bit of code will ever
be called outside the normal event thread, you can use the static method
SwingUtilities.isEventDispatchThread()
to test the identity of the current thread. You can then perform your
activity using the event-queue mechanism we’ll talk about next.
Finally, Swing components can be safely modified when the API
documentation explicitly says that the method is threadsafe. Many
important methods of Swing components are explicitly documented as
threadsafe. These include the JComponent
repaint()
and revalidate()
methods, many methods of Swing text components, and all listener add and
remove methods.
If you can’t meet any of the requirements for thread safety listed
previously, you can use a simple API to get the system event queue to
perform arbitrary activities for you on the event-handling thread. This is
accomplished using the invokeAndWait()
or
invokeLater()
static
methods of the javax.swing.SwingUtilities
class:
public static void invokeLater(Runnable doRun)
Use this method to ask Swing to execute the
run()
method of the specifiedRunnable
.public static void invokeAndWait(Runnable doRun)throwsInterruptedException,InvocationTargetException
This method is just like
invokeLater()
except that it waits until therun()
method has completed before returning.
You can put any activities you want inside a Runnable
object and cause the event dispatcher
to perform them using these methods. Often you’ll use an inner class; for
example:
SwingUtilities
.
invokeLater
(
new
Runnable
()
{
public
void
run
()
{
MyComponent
.
setVisible
(
false
);
}
}
);
Java 7 introduced the SwingWorker
class to
assist in situations where you have a background process that needs to
update a Swing UI after the process is complete or incrementally as it’s
running. In the former case, it’s a simple matter of subclassing SwingWorker
, and putting your long-running code
in doInBackground()
and your
UI update code in done()
.
package
learning
;
import
java.awt.BorderLayout
;
import
java.awt.event.ActionEvent
;
import
java.awt.event.ActionListener
;
import
javax.swing.*
;
public
class
MysteryOfTheUniverse
extends
JFrame
{
JTextArea
textArea
;
JButton
solveButton
;
public
MysteryOfTheUniverse
()
{
super
(
"Mystery of the Universe Solver"
);
setDefaultCloseOperation
(
JFrame
.
EXIT_ON_CLOSE
);
setSize
(
300
,
300
);
textArea
=
new
JTextArea
();
solveButton
=
new
JButton
(
"Solve Mystery"
);
solveButton
.
addActionListener
(
new
ActionListener
()
{
public
void
actionPerformed
(
ActionEvent
ae
)
{
solveButton
.
setEnabled
(
false
);
solveMysteryOfTheUniverse
();
}
});
add
(
solveButton
,
BorderLayout
.
NORTH
);
add
(
new
JScrollPane
(
textArea
));
add
(
new
JButton
(
"Click me! I'm not blocking."
),
BorderLayout
.
SOUTH
);
}
public
void
solveMysteryOfTheUniverse
()
{
(
new
MysteryWorker
()).
execute
();
}
class
MysteryWorker
extends
SwingWorker
<
String
,
Object
>
{
@Override
public
String
doInBackground
()
{
// Thinking for 4 seconds, but not blocking the UI
try
{
Thread
.
currentThread
().
sleep
(
4000
);
}
catch
(
InterruptedException
ignore
)
{}
solveButton
.
setEnabled
(
true
);
return
"Egg salad"
;
}
@Override
protected
void
done
()
{
try
{
textArea
.
setText
(
get
());
}
catch
(
Exception
ignore
)
{}
}
}
public
static
void
main
(
String
[]
args
)
{
new
MysteryOfTheUniverse
().
setVisible
(
true
);
}
}
When you click the SwingWorker
, the event dispatch thread would
block, making the button
at the bottom of the screen unclickable. Thanks to SwingWorker
, the UI remains usable during the
time the background task is executing. Don’t trust us. Try it!
SwingWorker
can be used in more
complex situations such as incremental updates of a progress bar. See
SwingWorker
’s JavaDoc introduction for
an example of this usage.
You may find that you won’t have to use the event dispatcher or
SwingWorker
in simple GUI applications
because most activity happens in response to user interface events where
it is safe to modify components. However, consider these caveats when you
create threads to perform long-running tasks such as loading data or
communicating over the network.
Get Learning Java, 4th Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.