An abstract class to perform lengthy GUI-interacting tasks in a
dedicated thread.
When writing a multi-threaded application using Swing, there are
two constraints to keep in mind:
(refer to
How to Use Threads
for more details):
- Time-consuming tasks should not be run on the Event
Dispatch Thread. Otherwise the application becomes unresponsive.
- Swing components should be accessed on the Event
Dispatch Thread only.
These constraints mean that a GUI application with time intensive
computing needs at least two threads: 1) a thread to perform the lengthy
task and 2) the Event Dispatch Thread (EDT) for all GUI-related
activities. This involves inter-thread communication which can be
tricky to implement.
{@code SwingWorker} is designed for situations where you need to have a long
running task run in a background thread and provide updates to the UI
either when done, or while processing.
Subclasses of {@code SwingWorker} must implement
the {@link #doInBackground} method to perform the background computation.
Workflow
There are three threads involved in the life cycle of a
{@code SwingWorker} :
-
Current thread: The {@link #execute} method is
called on this thread. It schedules {@code SwingWorker} for the execution on a
worker
thread and returns immediately. One can wait for the {@code SwingWorker} to
complete using the {@link #get get} methods.
-
Worker thread: The {@link #doInBackground}
method is called on this thread.
This is where all background activities should happen. To notify
{@code PropertyChangeListeners} about bound properties changes use the
{@link #firePropertyChange firePropertyChange} and
{@link #getPropertyChangeSupport} methods. By default there are two bound
properties available: {@code state} and {@code progress}.
-
Event Dispatch Thread: All Swing related activities occur
on this thread. {@code SwingWorker} invokes the
{@link #process process} and {@link #done} methods and notifies
any {@code PropertyChangeListeners} on this thread.
Often, the Current thread is the Event Dispatch
Thread.
Before the {@code doInBackground} method is invoked on a worker thread,
{@code SwingWorker} notifies any {@code PropertyChangeListeners} about the
{@code state} property change to {@code StateValue.STARTED}. After the
{@code doInBackground} method is finished the {@code done} method is
executed. Then {@code SwingWorker} notifies any {@code PropertyChangeListeners}
about the {@code state} property change to {@code StateValue.DONE}.
{@code SwingWorker} is only designed to be executed once. Executing a
{@code SwingWorker} more than once will not result in invoking the
{@code doInBackground} method twice.
Sample Usage
The following example illustrates the simplest use case. Some
processing is done in the background and when done you update a Swing
component.
Say we want to find the "Meaning of Life" and display the result in
a {@code JLabel}.
final JLabel label;
class MeaningOfLifeFinder extends SwingWorker<String, Object> {
{@code @Override}
public String doInBackground() {
return findTheMeaningOfLife();
}
{@code @Override}
protected void done() {
try {
label.setText(get());
} catch (Exception ignore) {
}
}
}
(new MeaningOfLifeFinder()).execute();
The next example is useful in situations where you wish to process data
as it is ready on the Event Dispatch Thread.
Now we want to find the first N prime numbers and display the results in a
{@code JTextArea}. While this is computing, we want to update our
progress in a {@code JProgressBar}. Finally, we also want to print
the prime numbers to {@code System.out}.
class PrimeNumbersTask extends
SwingWorker<List<Integer>, Integer> {
PrimeNumbersTask(JTextArea textArea, int numbersToFind) {
//initialize
}
{@code @Override}
public List<Integer> doInBackground() {
while (! enough && ! isCancelled()) {
number = nextPrimeNumber();
publish(number);
setProgress(100 * numbers.size() / numbersToFind);
}
}
return numbers;
}
{@code @Override}
protected void process(List<Integer> chunks) {
for (int number : chunks) {
textArea.append(number + "\n");
}
}
}
JTextArea textArea = new JTextArea();
final JProgressBar progressBar = new JProgressBar(0, 100);
PrimeNumbersTask task = new PrimeNumbersTask(textArea, N);
task.addPropertyChangeListener(
new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
if ("progress".equals(evt.getPropertyName())) {
progressBar.setValue((Integer)evt.getNewValue());
}
}
});
task.execute();
System.out.println(task.get()); //prints all prime numbers we have got
Because {@code SwingWorker} implements {@code Runnable}, a
{@code SwingWorker} can be submitted to an
{@link java.util.concurrent.Executor} for execution. |