FileDocCategorySizeDatePackage
SwingWorker.javaAPI DocJava SE 6 API30446Tue Jun 10 00:26:40 BST 2008javax.swing

SwingWorker

public abstract class SwingWorker extends Object implements RunnableFuture
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.

author
Igor Kushnirskiy
version
1.8 11/30/06
param
the result type returned by this {@code SwingWorker's} {@code doInBackground} and {@code get} methods
param
the type used for carrying out intermediate results by this {@code SwingWorker's} {@code publish} and {@code process} methods
since
1.6

Fields Summary
private static final int
MAX_WORKER_THREADS
number of worker threads.
private volatile int
progress
current progress.
private volatile StateValue
state
current state.
private final FutureTask
future
everything is run inside this FutureTask. Also it is used as a delegatee for the Future API.
private final PropertyChangeSupport
propertyChangeSupport
all propertyChangeSupport goes through this.
private AccumulativeRunnable
doProcess
handler for {@code process} mehtod.
private AccumulativeRunnable
doNotifyProgressChange
handler for progress property change notifications.
private final AccumulativeRunnable
doSubmit
private static final Object
DO_SUBMIT_KEY
Constructors Summary
public SwingWorker()
Constructs this {@code SwingWorker}.


                  
       
                     
        
                           
        

                            
        
    

             
      
        Callable<T> callable = 
                new Callable<T>() {
                    public T call() throws Exception {
                        setState(StateValue.STARTED);
                        return doInBackground();
                    }
                };

        future = new FutureTask<T>(callable) {
                       @Override
                       protected void done() {
                           doneEDT();
                           setState(StateValue.DONE);
                       }
                   };

       state = StateValue.PENDING;
       propertyChangeSupport = new SwingWorkerPropertyChangeSupport(this);
       doProcess = null;
       doNotifyProgressChange = null;
    
Methods Summary
public final voidaddPropertyChangeListener(java.beans.PropertyChangeListener listener)
Adds a {@code PropertyChangeListener} to the listener list. The listener is registered for all properties. The same listener object may be added more than once, and will be called as many times as it is added. If {@code listener} is {@code null}, no exception is thrown and no action is taken.

Note: This is merely a convenience wrapper. All work is delegated to {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.

param
listener the {@code PropertyChangeListener} to be added

        getPropertyChangeSupport().addPropertyChangeListener(listener);
    
public final booleancancel(boolean mayInterruptIfRunning)
{@inheritDoc}

        return future.cancel(mayInterruptIfRunning);
    
protected abstract TdoInBackground()
Computes a result, or throws an exception if unable to do so.

Note that this method is executed only once.

Note: this method is executed in a background thread.

return
the computed result
throws
Exception if unable to compute a result

protected voiddone()
Executed on the Event Dispatch Thread after the {@code doInBackground} method is finished. The default implementation does nothing. Subclasses may override this method to perform completion actions on the Event Dispatch Thread. Note that you can query status inside the implementation of this method to determine the result of this task or whether this task has been cancelled.

see
#doInBackground
see
#isCancelled()
see
#get

    
private voiddoneEDT()
Invokes {@code done} on the EDT.

        Runnable doDone = 
            new Runnable() {
                public void run() {
                    done();
                }
            };
        if (SwingUtilities.isEventDispatchThread()) {
            doDone.run();
        } else {
            doSubmit.add(doDone);
        }
    
public final voidexecute()
Schedules this {@code SwingWorker} for execution on a worker thread. There are a number of worker threads available. In the event all worker threads are busy handling other {@code SwingWorkers} this {@code SwingWorker} is placed in a waiting queue.

Note: {@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.

        getWorkersExecutorService().execute(this);
    
public final voidfirePropertyChange(java.lang.String propertyName, java.lang.Object oldValue, java.lang.Object newValue)
Reports a bound property update to any registered listeners. No event is fired if {@code old} and {@code new} are equal and non-null.

This {@code SwingWorker} will be the source for any generated events.

When called off the Event Dispatch Thread {@code PropertyChangeListeners} are notified asynchronously on the Event Dispatch Thread.

Note: This is merely a convenience wrapper. All work is delegated to {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.

param
propertyName the programmatic name of the property that was changed
param
oldValue the old value of the property
param
newValue the new value of the property

        getPropertyChangeSupport().firePropertyChange(propertyName,
            oldValue, newValue);
    
public final Tget()
{@inheritDoc}

Note: calling {@code get} on the Event Dispatch Thread blocks all events, including repaints, from being processed until this {@code SwingWorker} is complete.

When you want the {@code SwingWorker} to block on the Event Dispatch Thread we recommend that you use a modal dialog.

For example:

class SwingWorkerCompletionWaiter extends PropertyChangeListener {
private JDialog dialog;

public SwingWorkerCompletionWaiter(JDialog dialog) {
this.dialog = dialog;
}

public void propertyChange(PropertyChangeEvent event) {
if ("state".equals(event.getPropertyName())
&& SwingWorker.StateValue.DONE == event.getNewValue()) {
dialog.setVisible(false);
dialog.dispose();
}
}
}
JDialog dialog = new JDialog(owner, true);
swingWorker.addPropertyChangeListener(
new SwingWorkerCompletionWaiter(dialog));
swingWorker.execute();
//the dialog will be visible until the SwingWorker is done
dialog.setVisible(true);

        return future.get();
    
public final Tget(long timeout, java.util.concurrent.TimeUnit unit)
{@inheritDoc}

Please refer to {@link #get} for more details.

        return future.get(timeout, unit);
    
private static sun.swing.AccumulativeRunnablegetDoSubmit()

        
        synchronized (DO_SUBMIT_KEY) {
            final AppContext appContext = AppContext.getAppContext();
            Object doSubmit = appContext.get(DO_SUBMIT_KEY);
            if (doSubmit == null) {
                doSubmit = new DoSubmitAccumulativeRunnable();
                appContext.put(DO_SUBMIT_KEY, doSubmit);
            } 
            return (AccumulativeRunnable<Runnable>) doSubmit;
        }
    
public final intgetProgress()
Returns the {@code progress} bound property.

return
the progress bound property.

        return progress;
    
public final java.beans.PropertyChangeSupportgetPropertyChangeSupport()
Returns the {@code PropertyChangeSupport} for this {@code SwingWorker}. This method is used when flexible access to bound properties support is needed.

This {@code SwingWorker} will be the source for any generated events.

Note: The returned {@code PropertyChangeSupport} notifies any {@code PropertyChangeListener}s asynchronously on the Event Dispatch Thread in the event that {@code firePropertyChange} or {@code fireIndexedPropertyChange} are called off the Event Dispatch Thread.

return
{@code PropertyChangeSupport} for this {@code SwingWorker}

        return propertyChangeSupport;
    
public final javax.swing.SwingWorker$StateValuegetState()
Returns the {@code SwingWorker} state bound property.

return
the current state

        /*
         * DONE is a speacial case
         * to keep getState and isDone is sync
         */
        if (isDone()) {
            return StateValue.DONE;
        } else {
            return state;
        }
    
private static synchronized java.util.concurrent.ExecutorServicegetWorkersExecutorService()
returns workersExecutorService. returns the service stored in the appContext or creates it if necessary. If the last one it triggers autoShutdown thread to get started.

return
ExecutorService for the {@code SwingWorkers}
see
#startAutoShutdownThread

        final AppContext appContext = AppContext.getAppContext();
        Object obj = appContext.get(SwingWorker.class);
        if (obj == null) {
            //this creates non-daemon threads. 
            ThreadFactory threadFactory = 
                new ThreadFactory() {
                    final ThreadFactory defaultFactory = 
                        Executors.defaultThreadFactory();
                    public Thread newThread(final Runnable r) {
                        Thread thread = 
                            defaultFactory.newThread(r);
                        thread.setName("SwingWorker-" 
                            + thread.getName());
                        return thread;
                    }
                };

            /*
             * We want a to have no more than MAX_WORKER_THREADS
             * running threads.
             *
             * We want a worker thread to wait no longer than 1 second
             * for new tasks before terminating.
             */
            obj = new ThreadPoolExecutor(0, MAX_WORKER_THREADS,
                                         1L, TimeUnit.SECONDS,
                                         new LinkedBlockingQueue<Runnable>(),
                                         threadFactory) {

                    private final ReentrantLock pauseLock = new ReentrantLock();
                    private final Condition unpaused = pauseLock.newCondition();
                    private boolean isPaused = false;
                    private final ReentrantLock executeLock = new ReentrantLock();
                    
                    @Override
                    public void execute(Runnable command) {
                        /*
                         * ThreadPoolExecutor first tries to run task
                         * in a corePool. If all threads are busy it
                         * tries to add task to the waiting queue. If it
                         * fails it run task in maximumPool.
                         *
                         * We want corePool to be 0 and
                         * maximumPool to be MAX_WORKER_THREADS
                         * We need to change the order of the execution.
                         * First try corePool then try maximumPool
                         * pool and only then store to the waiting
                         * queue. We can not do that because we would
                         * need access to the private methods.
                         *
                         * Instead we enlarge corePool to
                         * MAX_WORKER_THREADS before the execution and
                         * shrink it back to 0 after. 
                         * It does pretty much what we need.
                         *
                         * While we changing the corePoolSize we need
                         * to stop running worker threads from accepting new
                         * tasks.
                         */
                        
                        //we need atomicity for the execute method.
                        executeLock.lock();
                        try {

                            pauseLock.lock();
                            try {
                                isPaused = true;
                            } finally {
                                pauseLock.unlock();
                            }
                            
                            setCorePoolSize(MAX_WORKER_THREADS);
                            super.execute(command);
                            setCorePoolSize(0);
                            
                            pauseLock.lock();
                            try {
                                isPaused = false;
                                unpaused.signalAll();
                            } finally {
                                pauseLock.unlock();
                            }
                        } finally {
                            executeLock.unlock();
                        }
                    }
                    @Override 
                    protected void afterExecute(Runnable r, Throwable t) { 
                        super.afterExecute(r, t);
                        pauseLock.lock();
                        try {
                            while(isPaused) {
                                unpaused.await();
                            }
                        } catch(InterruptedException ignore) {
                            
                        } finally {
                            pauseLock.unlock();
                        }
                    }
                };
            appContext.put(SwingWorker.class, obj);
        }
        return (ExecutorService)obj; 
    
public final booleanisCancelled()
{@inheritDoc}

        return future.isCancelled();
    
public final booleanisDone()
{@inheritDoc}

        return future.isDone();
    
protected voidprocess(java.util.List chunks)
Receives data chunks from the {@code publish} method asynchronously on the Event Dispatch Thread.

Please refer to the {@link #publish} method for more details.

param
chunks intermediate results to process
see
#publish

    
protected final voidpublish(V chunks)
Sends data chunks to the {@link #process} method. This method is to be used from inside the {@code doInBackground} method to deliver intermediate results for processing on the Event Dispatch Thread inside the {@code process} method.

Because the {@code process} method is invoked asynchronously on the Event Dispatch Thread multiple invocations to the {@code publish} method might occur before the {@code process} method is executed. For performance purposes all these invocations are coalesced into one invocation with concatenated arguments.

For example:

publish("1");
publish("2", "3");
publish("4", "5", "6");
might result in:
process("1", "2", "3", "4", "5", "6")

Sample Usage. This code snippet loads some tabular data and updates {@code DefaultTableModel} with it. Note that it safe to mutate the tableModel from inside the {@code process} method because it is invoked on the Event Dispatch Thread.

class TableSwingWorker extends
SwingWorker<DefaultTableModel, Object[]> {
private final DefaultTableModel tableModel;

public TableSwingWorker(DefaultTableModel tableModel) {
this.tableModel = tableModel;
}

{@code @Override}
protected DefaultTableModel doInBackground() throws Exception {
for (Object[] row = loadData();
! isCancelled() && row != null;
row = loadData()) {
publish((Object[]) row);
}
return tableModel;
}

{@code @Override}
protected void process(List<Object[]> chunks) {
for (Object[] row : chunks) {
tableModel.addRow(row);
}
}
}

param
chunks intermediate results to process
see
#process

        synchronized (this) {
            if (doProcess == null) {
                doProcess = new AccumulativeRunnable<V>() {
                    @Override
                    public void run(List<V> args) {
                        process(args);
                    }
                    @Override
                    protected void submit() {
                        doSubmit.add(this);
                    }
                };
            }
        }
        doProcess.add(chunks);
    
public final voidremovePropertyChangeListener(java.beans.PropertyChangeListener listener)
Removes a {@code PropertyChangeListener} from the listener list. This removes a {@code PropertyChangeListener} that was registered for all properties. If {@code listener} was added more than once to the same event source, it will be notified one less time after being removed. If {@code listener} is {@code null}, or was never added, no exception is thrown and no action is taken.

Note: This is merely a convenience wrapper. All work is delegated to {@code PropertyChangeSupport} from {@link #getPropertyChangeSupport}.

param
listener the {@code PropertyChangeListener} to be removed

        getPropertyChangeSupport().removePropertyChangeListener(listener);
    
public final voidrun()
Sets this {@code Future} to the result of computation unless it has been cancelled.

        future.run();
    
protected final voidsetProgress(int progress)
Sets the {@code progress} bound property. The value should be from 0 to 100.

Because {@code PropertyChangeListener}s are notified asynchronously on the Event Dispatch Thread multiple invocations to the {@code setProgress} method might occur before any {@code PropertyChangeListeners} are invoked. For performance purposes all these invocations are coalesced into one invocation with the last invocation argument only.

For example, the following invokations:

setProgress(1);
setProgress(2);
setProgress(3);
might result in a single {@code PropertyChangeListener} notification with the value {@code 3}.

param
progress the progress value to set
throws
IllegalArgumentException is value not from 0 to 100

        if (progress < 0 || progress > 100) {
            throw new IllegalArgumentException("the value should be from 0 to 100");
        }
        if (this.progress == progress) {
            return;
        }
        int oldProgress = this.progress;
        this.progress = progress;
        if (! getPropertyChangeSupport().hasListeners("progress")) {
            return;
        }
        synchronized (this) {
            if (doNotifyProgressChange == null) {
                doNotifyProgressChange = 
                    new AccumulativeRunnable<Integer>() {
                        @Override
                        public void run(List<Integer> args) {
                            firePropertyChange("progress", 
                               args.get(0), 
                               args.get(args.size() - 1));
                        }
                        @Override
                        protected void submit() {
                            doSubmit.add(this);
                        }
                    };
            }
        }
        doNotifyProgressChange.add(oldProgress, progress);
    
private voidsetState(javax.swing.SwingWorker$StateValue state)
Sets this {@code SwingWorker} state bound property.

param
the state state to set

        StateValue old = this.state;
        this.state = state;
        firePropertyChange("state", old, state);