views:

585

answers:

3

Hi guys,

I'm developing a small app, which would have Swing GUI. App is doing IO task in another thread, when that thread finishes GUI should be updated acordingly to reflect thread's operation result. Class running in a (worker, non-GUI) has object passed to it in contructor which would be used for updating GUI, so I don't need to put GUI stuff in a non-GUI class, but rather pass object for updating GUI to that class. As I understand form reading here, (thread/swing) safe options for updating (changing) Swing GUI would be to use javax.swing.SwingUtilities.invokeLater(), javax.swing.SwingUtilities.invokeLaterWait() and/or javax.swing.SwingWorker() which basically are doing the same thing.

This all threading issue with Swing is a little confusing for me, and yet I need to use threads to do anything meaningful in GUI apps and not hung GUI while processing in EDT, so what interests me for now is this:

  1. Are invokeLater and invokeLaterWait like sending message to EDT and waiting for it do it when it finishes processing messages that were before that call?

  2. is it correct from Swing thread safety aspect, to do something like this:

    interface IUPDATEGUI {  
      public void update();  
    }  
    
    
    // in EDT/where I can access components directly    
    class UpdateJList implements IUPDATEGUI {    
      public void update() {    
        // update JList...    
        someJList.revalidate();    
        someJList.repain();    
      }    
    }    
    
    
    class FileOperations implements Runnable {  
      private IUPDATEGUI upObj;  
      List<File> result = new ArrayList<File>; // upObject is accessing this  
    
    
      public void FileOperations(IUPDATEGUI upObj) {
        this.upObj = upObj;
      }
    
    
      private void someIOTask() {
        // ...
        // IO processing finished, result is in "result"
      }
    
    
      public void run() {
        someIOTask();
        javax.swing.SwingUtilities.invokeLater(new Runnable() {
          public void run() {
            upObj.update();  // access result and update JList
          }
        }; );
      }
    }
    

In case this isn't correct then how should this be done?

If I could, I would prefer to use invokeLater instead of SwingWorker if possible, because I wouldn't need to change my whole class and it's somehow more neat/distinct me (like sending a message in Win32 apps).

Thanks in advance.

A: 

invokeLater is fine. This puts the call into the AWT event queue, so that it will get executed in the EDT in due course. Your program will continue running, and does not wait for your callable to get called.

Chris Jester-Young
Are calls to revalidate() and repaint() necessary after updating component in this case?
Mil
+1  A: 

Using invokeLater() and invokeAndWait() passes the Runnable parameter into the queue awaiting execution in the EDT. So calling invokeLater() will cause the Runnable to execute in the EDT when the EDT is able to process the request. invokeAndWait() simply waits(in the calling thread) until this execution takes place.

Using swingWorker is ideal if you want to do background tasks that notify the EDT either at the end of execution or in intermmediate states. An example would be to pass the current progress of a process to a JProgressBar.

For your example it seems that swingWorker is a better choice but if you don't want to change your code too much then calling invokeLater() when the process is done will be just fine.

Savvas Dalkitsis
Can you please tell me more why do you think SwingerWorker would be a better choice in my case?
Mil
I think I got it. Please correct me if I'm wrong, basically I can update GUI components in both methods of SwingWorker class, that is for example I could update JProgressBar in doInBackground() and at the end when its finished, I could update JList in done() method of SwingWorker class?
Mil
when you want to post intermmediate updates to the GUI while the process is still running you use the publish() method of swingworker which invokes the process() method in the EDT. And the done() method is called when the process is finished also in the EDT. So you have two options to update the GUI. One while the process is running (publish) and when it is done (done). Read this : http://java.sun.com/javase/6/docs/api/javax/swing/SwingWorker.htmlit is really helpful.
Savvas Dalkitsis
Thanks a lot for explanation.
Mil
+2  A: 

I'd recommend not using the invokeAndWait until java 7. I found a spurious wake-up on this method that can cause really painful bugs. For me it led to some really rare and hard to debug null pointer exceptions.

http://bugs.sun.com/view_bug.do?bug_id=6852111

It's fixed as of java 7 b77.

reccles
+1 for posting about the spurious wakeup...the "wait in a loop" is a standard idiom for conditions, so I wonder why they haven't done it here to begin with.
Chris Jester-Young
I posted code to "fix" that years ago. There shouldn't actually be a problem because Sun JVMs never wake spuriously. If you see NPEs, it may be from other causes. / In any case, `invokeAndWait` tends to cause deadlocks so should be avoided.
Tom Hawtin - tackline
See this pair of old CRs: http://bugs.sun.com/view_bug.do?bug_id=4974934 http://bugs.sun.com/view_bug.do?bug_id=4932181
Tom Hawtin - tackline
Interesting. Are you 100% sure about the Sun JVM's never spuriously wake up? This contradicts a lot of what we have seen in prod.
reccles