6
public static void main(String args[]) {
    /* Set the Nimbus look and feel */
    //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
    /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
     * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
     */
    try {
        for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (ClassNotFoundException ex) {
        java.util.logging.Logger.getLogger(MyDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(MyDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(MyDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(MyDialog.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }
    //</editor-fold>

    /* Create and display the dialog */
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            MyDialog dialog = new MyDialog(new javax.swing.JFrame(), true);
            dialog.addWindowListener(new java.awt.event.WindowAdapter() {
                @Override
                public void windowClosing(java.awt.event.WindowEvent e) {
                    System.exit(0);
                }
            });
            dialog.setVisible(true);
        }
    });
}

The MyDialog class just has few combos and textfields and am populating combo with DB values. On selecting one combo value i fetch another value from DB to populate the next combo.

The above program runs the same way without using invokeLater threading. When does invokeLater becomes useful in Swing programming. I have read some about it, but all seems to be theoratical. What difference does the invokeLater makes to the application? Is it enough to use it just inside main method or it should also be used in action Listeners?

SwingUtilities.invokeLater and java.awt.EventQueue.invokeLater - are they same?

Alberto Zaccagni
  • 30,779
  • 11
  • 72
  • 106
Mohamed Iqzas
  • 976
  • 1
  • 14
  • 19
  • 6
    Have you read the [official documentation on Swing threading](http://docs.oracle.com/javase/tutorial/uiswing/concurrency/)? – Guillaume Aug 19 '13 at 10:51
  • 6
    It works because you're lucky. Race conditions due to thread timing are generally next to impossible to reproduce reliably. It's not unusual for code to appear to work correctly, but the same code may have issues on other systems, or on different jvms. The larger the program is the more probable it becomes that something breaks. `SwingUtilities.invokeLater()` calls `java.awt.EventQueue.invokeLater()`. – kiheru Aug 19 '13 at 10:55
  • 4
    Most of the events usually take place on EDT, but not all. Though you can always check this by using `SwingUtilities.isEventDispatchThread()`. Care must be taken at all times, as done in this [example](http://docs.oracle.com/javase/tutorial/displayCode.html?code=http://docs.oracle.com/javase/tutorial/uiswing/examples/components/TextComponentDemoProject/src/components/TextComponentDemo.java), do have a look inside `displaySelectionInfo(...)` method and please do read the comment above the same. Hope that gives you the faint idea about the whole thingy :-) – nIcE cOw Aug 19 '13 at 11:10
  • 4
    I learned my lesson the hard way when starting out with Swing. I completely ignored thread safety, and while my application looked just fine on Windows XP, it was completely broken on Vista. SwingUtilities.invokeLater() and I became friends soon afterwards. :-) – perp Aug 19 '13 at 14:05

1 Answers1

6

Nothing theoretical about it. It's very practical. The SwingUtilities.invokeLater() method guarantees that the code within the Runnable will run on the Event Dispatch Thread (EDT). This is important because Swing is not thread-safe, thus anything related to the GUI (Swing, etc.) needs to run on the EDT. The EDT is a "it happens whenever it happens" thread that makes no guarantees about the order in which things are executed. If the GUI code is executed within a background thread (say, within a SwingWorker instance), then it can throw errors. I learned this the hard way: in my learning years, executing GUI-changing code within a background thread caused random, inconsistent RuntimeExceptions that I couldn't figure out. It was a good learning experience (SwingWorker has a doInBackground() method for background tasks and a done() method for EDT tasks).

In the same way you don't want to execute GUI code on a background thread, you also don't want to execute large operations (database queries, etc) on the EDT. This is because the EDT is dispatching all of the GUI events so everything on the EDT should be very short and sweet. You can easily see this with a JProgressBar set to indeterminate.

This SSCCE should illustrate it nicely. Notice the motion of the JProgressBar as method() is called, once on a background thread and once on a EDT thread.

import javax.swing.JFrame;
import javax.swing.JProgressBar;
import javax.swing.SwingWorker;

/**
 *
 * @author Ryan
 */
public class Test {

    public static void main(String args[]) {
        JFrame frame = new JFrame();
        JProgressBar jpb = new JProgressBar();
        jpb.setIndeterminate(true);
        frame.add(jpb);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        new Task().execute();
    }

    public static void method() { // This is a method that does a time-consuming task.
        for(int i = 1; i <= 5; i++) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(i);
        }
    }

    static class Task extends SwingWorker<Void, Void> {

        @Override
        protected Void doInBackground() throws Exception {
            /* Executing method on background thread.
             * The loading bar should keep moving because, although method() is time consuming, we are on a background thread.
            */ 
            method();
            return null;
        }

        @Override
        protected void done() {
            /* Executing method on Event Dispatch Thread.
             * The loading bar should stop because method() is time consuming and everything on the Event Dispatch Thread
             * (like the motion of the progress bar) is waiting for it to finish.
            */

            // 
            method();
        }
    }
}

Hope this helps.

ryvantage
  • 13,064
  • 15
  • 63
  • 112
  • Nice example. But most of the requirements for me is to proceed with other actions, only if the ongoing operation is completed. In such an application how does it help to execute the work in background thread instead of EDT. Whether its in EDT or not, I want the user to do further actions only when current operation is completed. So I can show him a progress bar. When I do all the operation inside action listeners and methods called from action listeners. Here its being executed only on EDT. Didnt require Swing Worker at all. Please correct me If i am wrong. – Mohamed Iqzas Sep 13 '13 at 12:49
  • In that case, if you require user interaction ("Ok", "Cancel", etc) then you might need to chain multiple `SwingWorker`s together. So, you get initial input, then execute your code on a background thread, then ask for more input, then start another background thread, etc. It'll require multiple `SwingWorker`s. In situations like this, I try to be clever about how I get user input (i.e., can you get all the user input up front?) – ryvantage Sep 13 '13 at 17:56
  • yeah. User will provide all user inputs for that particular window/dialog in a single shot. Then need to show another window or result based on the operation. Let me ask plain : Is SwingWorker neccessary only to perform background tasks when the user is performing some other actions? – Mohamed Iqzas Sep 19 '13 at 10:48
  • 1
    Yes, unless you aren't using Swing. If you require Swing components to initiate, report about, stop, or otherwise interact with a long process that needs to be on background thread, SwingWorker is the easiest (and only?) option you have to easily manage whether code is executed on the EDT or a background thread. – ryvantage Sep 20 '13 at 10:59