Threading with Swing
You may have written a GUI-enabled application using a framework such as
Swing, and never even given a thought to threads. And for some types of application,
you pretty much don't have to. If everything in your application happens directly
in response to a button click or other Swing event, and your application never
"does things with threads" (it never sleeps, spawns another thread or process,
"does something in the background" etc), then be it by good design or good fortune, it may be
behaving correctly. But there are times, particularly if your application
does want to "do something with threads", when you need to understand how
threading works with respect to the GUI system.
As you'll know if you've programmed with GUI systems such as Swing, everything
generally happens in response to events. You define an event
handler, which is effectively the code you would like to be called when
something happens. For example, we can define a button like this:
JButton b = new JButton("Click me");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// Code will be performed when button clicked
}
});
To our program, the call to our method just "comes out of nowhere". But
in reality, it must come from somewhere. That is: there must be some particular
thread that's calling into our method. Well, there is, and it's quite
a well-defined thread.
The event dispatch thread
The thread making calls to our event handlers is the event dispatch thread.
This is a special thread that the GUI system sets up for performing UI tasks. Essentially, all
user interface code will be executed by this special thread. Having a single designated
thread handling the entire UI avoids a lot of issues that would occur if we tried to
allow, say, different event handlers to be called by arbitrary threads.
In most Swing applications, if you've not thought about it and done anything special
to use other threads, then practically all of your application will probably happen in
the event dispatch thread. What you might not have thought about is whether it's
all happening correctly...
Proper threading behaviour of GUI applications
There are essentially two rules of thumb that you need to remember:
- always manipulate your user interface from the event dispatch thread
(with one or two safe exceptions);
- never block or delay the event dispatch thread— in other words,
never call methods such as Thread.sleep(), Object.wait(),
Condition.await() inside an event handler.
In more detail, these two guidelines have a few implications:
- all tasks that we perform inside an event handler should be
instantaneous; we should not perform a long-running task
(such as making a database query) or call Thread.sleep()
or Object.wait() or similar calls
directly from an event handler;
- to perform such long-running tasks, we need to arrnage for them to
happen in another thread, e.g. by
starting a new thread specially for the task;
- we need to be wary of the more "subtle" library calls that might
cause our thread to wait: if we use one of the concurrency utilities such as
a ThreadPoolExecutor, we need to make sure
we use a variant that won't ever block waiting for room in the queue;
other slightly more obvious no-nos are awaiting a latch,
joining another thread, calling Future.get()... essentially
anything that "waits" for something cannot be called inside an event
handler;
- if we're in our other thread and need to update the UI (e.g. to report
progress to the user), we generally need to arrange for that update code— and
only the update code— to happen in the event dispatch thread;
- by "manipulating the UI", we mean calling methods on or changing the state
of any Swing components but
also modifying any objects they depend on such as table models,
cell renderers etc; firing events must also happen in the event dispatch
thread.
How do you make something happen in the event dispatch thread?
So far, we've glibly said that in certain cases we must
run something on the event dispatch thread. But how do we actually do that?
On the next page, we look at the special method
SwingUtilities.invokeLater() which provides
us with this functionality.
If you enjoy this Java programming article, please share with friends and colleagues. Follow the author on Twitter for the latest news and rants.
Editorial page content written by Neil Coffey. Copyright © Javamex UK 2021. All rights reserved.