3

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.

Amit
  • 33,847
  • 91
  • 226
  • 299
  • What kind of results do you want to publish to the GUI? Are they subtask specific results (e.g. what each subtask is doing), or aggregate results from all tasks (e.g. % complete across all tasks.) – mdma May 19 '10 at 20:07
  • I want to publish an `Object` to the GUI which will be used to populate a `JTable`. – Amit May 23 '10 at 09:58

4 Answers4

8

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
  • 4,989
  • 1
  • 25
  • 19
  • +1 for `CountDownLatch`, which I'd previously overlooked in your answer. – trashgod May 14 '10 at 10:37
  • using `latch.await()` ensures that changes made by sub-threads will be visible at the end of `doInBackground()`. – Justin May 19 '10 at 17:09
  • 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 May 19 '10 at 20:06
  • @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 May 20 '10 at 00:39
  • Hi Noel, I'm thinking about implementing a similar solution to this. I'm wondering if, 8 years later, the above code is still the best solution or would you make any changes to it? – Colm Bhandal May 18 '18 at 16:07
3

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
        }
    });
}
Matthieu BROUILLARD
  • 1,991
  • 1
  • 16
  • 25
  • @Matthieu - Can u elaborate with an example or provide some link? – Amit May 09 '10 at 12:00
  • @Yatendra Goel: See _Continuations as objects_. http://en.wikipedia.org/wiki/Continuation_passing_style#Continuations_as_objects – trashgod May 09 '10 at 13:42
  • 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 May 19 '10 at 17:06
  • @Justin: what do you mean? SwingUtilities works well and for a while already. – Matthieu BROUILLARD May 20 '10 at 06:40
  • 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 May 20 '10 at 13:40
  • 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 May 20 '10 at 17:22
2

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
  • 203,806
  • 29
  • 246
  • 1,045
  • Why `EventQueue.invokeLater()` and not `SwingUtilities.invokeLater()`? – Amit May 14 '10 at 10:09
  • "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 May 14 '10 at 10:24
0

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
  • 3,521
  • 12
  • 45
  • 53
  • 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` – Amit May 13 '10 at 18:46
  • @Yatendra Goel: There's a source-compatible back-port to Java 5. https://swingworker.dev.java.net/ – trashgod May 13 '10 at 21:33