Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swing Thread Safe Programming

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?

like image 569
Mohamed Iqzas Avatar asked Aug 19 '13 10:08

Mohamed Iqzas


1 Answers

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.

like image 111
ryvantage Avatar answered Oct 17 '22 08:10

ryvantage