13

My understanding is that if I start up another thread to perform some actions, I would need to SwingUtilities.invokeAndWait or SwingUtilities.invokeLater to update the GUI while I'm in said thread. Please correct me if I'm wrong.

What I'm trying to accomplish is relatively straightforward: when the user clicks submit, I want to (before performing any actions) disable the submit button, perform the action, and at the end of the action re-enable the button. My method to perform the action updates the GUI directly (displays results) when it gets the results back.

This action basically queries a server and gets some results back.

What I have so far is:

boolean isRunning = false;

synchronized handleButtonClick() {
  if ( isRunning == false ) {
    button.setEnabled( false );
    isRunning = true;
    doAction();
  }
}

doAction() {
  new Thread() {
    try {
      performAction(); // Concern A
    } catch ( ... ) {
      displayStackTrace( ... ); // Concern B
    } finally {
      SwingUtilities.invokeLater ( /* simple Runnable to enable button */ );
      isRunning = false;
    }
  }
}

For both of my concerns above, do I would have to use SwingUtilities.invokeAndWait since they both will update the GUI? All GUI updates revolve around updating JTextPane. Do I need to in my thread check if I'm on EDT and if so I can call my code (regardless of whether it updates the GUI or not) and NOT use SwingUtilities.invokeAndWait?

EDIT: Here is what I am doing now:

handleButtonClick() {
  if ( isRunning == true )
     return;
  disable button;
  SwingWorker task = new MyTask();
  task.execute();
}

...inside MyTask
doInBackground() {
  return performAction();
}

done() {
  result = get();
  enable button;
  isRunning = false;
  interpret result (do most of the GUI updates here);
}

While performAction() does some GUI updates, I have wrapped those in:

if ( SwingUtil.isEDT() )
  doGUIupdate()
else
  SwingUtil.invokeLater( new Runnable() {
    run() {
      doGUIupdate();
    }
  } );

Hopefully this is a step in the right direction, please comment if you believe there are better ways to handle my situation.

nevets1219
  • 7,692
  • 4
  • 32
  • 47
  • 1
    Use SwingWorker. http://java.sun.com/javase/6/docs/api/javax/swing/SwingWorker.html – Taylor Leese Apr 02 '10 at 00:13
  • I was under the impression that SwingWorker should be used for something "long" whereas SwingUtilities was used for something "quick"? – nevets1219 Apr 02 '10 at 00:19
  • Because SwingUtilities executes on the "UI" thread, it would have to be used for something quick to be practical; else it could block the UI. Neither will execute in "another thread". – Justin Ethier Apr 02 '10 at 00:25

3 Answers3

18

In my opinion you should almost never use invokeAndWait(). If something is going to take awhile that will lock your UI.

Use a SwingWorker for this kind of thing. Take a look at Improve Application Performance With SwingWorker in Java SE 6.

PhiLho
  • 40,535
  • 6
  • 96
  • 134
cletus
  • 616,129
  • 168
  • 910
  • 942
  • Great link, half way through and it has revealed quite a bit to me! – nevets1219 Apr 02 '10 at 00:39
  • 1
    invokeAndWait() doesn't block the UI - it blocks the helper thread until the UI has had a chance to run the passed-in Runnable. invokeAndWait() actually checks to be sure it's not being called from the EDT (which would cause the lock). Of course I should say that use of invokeAndWait() usually means the programmer isn't properly using event-driven (observer) programming... – Scott Stanchfield Apr 02 '10 at 15:19
  • 1
    Using `invokeAndWait` often leads to deadlock. – Tom Hawtin - tackline Apr 02 '10 at 17:57
  • Dead link, please consider updating it! – Redandwhite Oct 03 '12 at 20:03
  • 1
    @Redandwhite thanks to indicate the dead link. Note: I found the article by pasting its name in the Oracle page found at the old link... – PhiLho Oct 23 '12 at 12:40
5

You should consider using SwingWorker since it will not block the UI thread, whereas both SwingUtilities methods will execute on the EDT thread, thus blocking the UI.

Justin Ethier
  • 131,333
  • 52
  • 229
  • 284
  • 1
    SwingWorker executes its done() method on the EDT. It's the same as starting your own non-EDT thread which calls SwingWorker.invokeLater() to update the UI when you're done. SwingWorker is just a little more convenient to use than rolling it yourself. – Scott Stanchfield Apr 02 '10 at 15:16
  • 1
    @Scott did you mean SwingUtilities? – nevets1219 Apr 02 '10 at 16:24
  • D'oh! That should be SwingUtilities.invokeLater(). Thanks for the catch. I do agree that SwingWorker is preferred now though. – Scott Stanchfield Apr 09 '10 at 15:39
0

I keep the simple Thread inside EventQueue.invokeLater(...) and that worked smoothly...

java.awt.EventQueue.invokeLater(new Runnable() {
    public void run(){

        new Thread(new Runnable(){
            public void run(){

                try{
                    EdgeProgress progress = EdgeProgress.getEdgeProgress();
                    System.out.println("now in traceProgressMonitor...");
                    while(true){
                        // here the swing update
                        if(monitor.getState() == ProgressMonitor.STATE_BUSY){
                            System.out.println(monitor.getPercentDone()/2);
                            progress.setProgress(monitor.getPercentDone()/2);
                        }else{
                            break;
                        }
                        Thread.sleep(5);
                    }
                }catch(InterruptedException ie){}

            }
        }).start();

    }
});
ArifMustafa
  • 4,617
  • 5
  • 40
  • 48