Swing Thread Безопасное программирование

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);
        }
    });
}

В классе MyDialog всего несколько комбо и текстовых полей, и я заполняю комбо значениями БД. При выборе одного комбо-значения я получаю другое значение из БД для заполнения следующего комбо.

Вышеприведенная программа работает аналогичным образом, без использования invokeLater. Когда invokeLater становится полезным в программировании на Swing. Я читал об этом немного, но все кажется теоретическим. Какое значение имеет invokeLater для приложения? Достаточно ли использовать его только внутри метода main или его также следует использовать в слушателях действий?

SwingUtilities.invokeLater и java.awt.EventQueue.invokeLater - они одинаковы?

1 ответ

Решение

Ничего теоретического в этом нет. Это очень практично. SwingUtilities.invokeLater() метод гарантирует, что код внутри Runnable будет работать на Event Dispatch Thread (EDT), Это важно, потому что Swing не является потокобезопасным, поэтому все, что связано с GUI (Swingи т. д.) должен работать на EDT, EDT является потоком "это случается всякий раз, когда это происходит", который не дает никаких гарантий относительно порядка выполнения вещей. Если код GUI выполняется в фоновом потоке (скажем, в SwingWorker экземпляр), то он может выдавать ошибки. Я усвоил это нелегким путем: в годы обучения выполнение кода, изменяющего GUI, в фоновом потоке вызывало случайное, противоречивое RuntimeExceptionс, что я не мог понять. Это был хороший учебный опыт (SwingWorker имеет doInBackground() метод для фоновых задач и done() метод для EDT задачи).

Точно так же вы не хотите выполнять код GUI в фоновом потоке, вы также не хотите выполнять большие операции (запросы к базе данных и т. Д.) На EDT, Это потому что EDT отправляет все события GUI, так что все на EDT должно быть очень коротким и сладким. Вы можете легко увидеть это с JProgressBar установить неопределенным.

Этот SSCCE должен хорошо это иллюстрировать. Обратите внимание на движение JProgressBar как method() вызывается один раз в фоновом потоке и один раз в EDT нить.

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();
        }
    }
}

Надеюсь это поможет.

Другие вопросы по тегам