I have a SwingWorker
as follows:
public class MainWorker extends SwingWorker(Void, MyObject) {
:
:
}
I invoked the above Swing 开发者_如何学JAVAWorker
from EDT:
MainWorker mainWorker = new MainWorker();
mainWorker.execute();
Now, the mainWorker
creates 10 instances of a MyTask
class so that each instance will run on its own thread so as to complete the work faster.
But the problem is I want to update the gui from time to time while the tasks are running. I know that if the task was executed by the mainWorker
itself, I could have used publish()
and process()
methods to update the gui.
But as the tasks are executed by threads different from the Swingworker
thread, how can I update the gui from intermediate results generated by threads executing tasks.
The SwingWorker's API documentation offers this hint:
The doInBackground() method is called on this thread. This is where all background activities should happen. To notify PropertyChangeListeners about bound properties changes use the firePropertyChange and getPropertyChangeSupport() methods. By default there are two bound properties available: state and progress.
MainWorker
can implement PropertyChangeListener
. It can then register itself with its PropertyChangeSupport
:
getPropertyChangeSupport().addPropertyChangeListener( this );
MainWorker
can supply its PropertyChangeSupport
object to every MyTask
object it creates.
new MyTask( ..., this.getPropertyChangeSupport() );
A MyTask
object can then notify its MainWorker
of progress or property updates by using PropertyChangeSupport.firePropertyChange
methods.
MainWorker
, so notified, can then use SwingUtilities.invokeLater
or SwingUtilities.invokeAndWait
to update the Swing components via the EDT.
protected Void doInBackground() {
final int TASK_COUNT = 10;
getPropertyChangeSupport().addPropertyChangeListener(this);
CountDownLatch latch = new CountDownLatch( TASK_COUNT ); // java.util.concurrent
Collection<Thread> threads = new HashSet<Thread>();
for (int i = 0; i < TASK_COUNT; i++) {
MyTask task = new MyTask( ..., latch, this.getPropertyChangeSupport() ) );
threads.add( new Thread( task ) );
}
for (Thread thread: threads) {
thread.start();
}
latch.await();
return null;
}
Even if you do not use SwingWorker, you can always post things to do in the EDT using SwingUtilities.invokeLater(...) or SwingUtilities.invokeAndWait(...)
EDIT: suppose that you have a thread executing some code, you can always interact with EDT like in the example below.
public void aMethodExecutedInAThread() {
// Do some computation, calculation in a separated Thread
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Post information in the EDT
// This code is executed inside EDT
}
});
}
Here is an example that uses a SwingWorker
to launch multiple threads. A CountDownLatch
ensures that doInBackground()
returns only when all threads have completed. Each thread uses the thread-safe append()
method of JTextArea
to update the GUI, but EventQueue.invokeLater()
would be a convenient alternative.
Read these artcles to get a clear picture of your problem
Threads and Swing
Using a Swing Worker Thread
The last word in Swing Threads
精彩评论