views:

339

answers:

4

I have a SwingWorker as follows:

public class MainWorker extends SwingWorker(Void, MyObject) {
    :
    :
}

I invoked the above Swing Worker 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.

+3  A: 
Matthieu BROUILLARD
@Matthieu - Can u elaborate with an example or provide some link?
Yatendra Goel
@Yatendra Goel: See _Continuations as objects_. http://en.wikipedia.org/wiki/Continuation_passing_style#Continuations_as_objects
trashgod
@Yatendra: I have edited to give you some example.
Matthieu BROUILLARD
Be careful about memory consistency effects here, changes made by aMethodExecutedInAThread() may not be visible by the EDT, unless the EDT synchronizes on the object which contains the changes (or the properties modified are marked as volatile.
Justin
@Justin: what do you mean? SwingUtilities works well and for a while already.
Matthieu BROUILLARD
Actions (memory writes) taken by aMethodExecutedInAThrad() need a happens-before relationship to the anonymous runnable (running in the EDT). Its actually not clear from `EventQueue` (or `SwingUtilities`) that `invokeLater()` and the EDT aquire the same lock (through they probably do in internally)
Justin
Heu... invokeLater() goal is to be executed in EDT without any collision with other EDT stuff, isn't it? I guess I have missed something from your comment.
Matthieu BROUILLARD
A: 

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

rgksugan
@rgksugan The `SwingWorker` mentioned in all of the above articles is obsolete. Since java 1.6, it has been part of jdk and it is not similar to the previous `SwingWorker`
Yatendra Goel
@Yatendra Goel: There's a source-compatible back-port to Java 5. https://swingworker.dev.java.net/
trashgod
+3  A: 

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;
}
Noel Ang
+1 for `CountDownLatch`, which I'd previously overlooked in your answer.
trashgod
using `latch.await()` ensures that changes made by sub-threads will be visible at the end of `doInBackground()`.
Justin
Assuming the tasks are independent, how do you communicate which task the update is coming from via firePropertyChange? E.g. say each task updates it's % complete, how can the background thread compute the total % complete from all tasks?
mdma
@mdma The information to perform that computation can be supplied by the PropertyChangeEvent emitters; e.g., as a bean representing the tuple (task-name, percentage-complete, percentage-of-total-work). firePropertyChange takes java.lang.Object for the property values.
Noel Ang
+1  A: 

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.

trashgod
@trashgod -- Why `EventQueue.invokeLater()` and not `SwingUtilities.invokeLater()`?
Yatendra Goel
"As of 1.3 this method is just a cover for java.awt.EventQueue.invokeLater()." http://java.sun.com/javase/6/docs/api/javax/swing/SwingUtilities.html#invokeLater%28java.lang.Runnable%29
trashgod