簡體   English   中英

Java從另一個線程更新UI組件

[英]java updating UI components from another thread

我找到了有關該問題的許多答案,但我仍然不明白為什么我的應用程序不會引發任何異常。 我在NetBeans 8中創建了一個新的Java表單應用程序。我的表單已創建並顯示在以下主要方法中:

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(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        catch (InstantiationException ex)
        {
            java.util.logging.Logger.getLogger(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        catch (IllegalAccessException ex)
        {
            java.util.logging.Logger.getLogger(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        catch (javax.swing.UnsupportedLookAndFeelException ex)
        {
            java.util.logging.Logger.getLogger(MainForm.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                new MainForm().setVisible(true);     
            }
        });
    }

因此,此新的Runnable將創建新的MainForm並將其設置為可見。

然后,在我的代碼中,我啟動了新線程,這些線程更新了一些jButton和jTextField。 代碼如下:

private void updateUI() {
        updateUIThread = new Thread(() ->
        { 
            while (true) {
                try {
                    jtfIP.setEnabled(!Start && !autoRec);
                    jtfPort.setEnabled(!Start && !autoRec);
                    jtfSlaveID.setEnabled(!Start && !autoRec);
                    jtfTimeout.setEnabled(!Start && !autoRec);
                    jtfReqInterval.setEnabled(!Start && !autoRec);
                    jCheckBox1.setEnabled(!Start && !autoRec);
                    jCBReconnect.setEnabled(!Start && !autoRec);

                    if (db != null) {
                        if (!db.getIsOpen()) {
                            jPBD.setBackground(Color.RED);
                            jPBD.setForeground(Color.WHITE);
                            jPBD.setText("ER");
                        } else {
                            jPBD.setBackground(Color.GREEN);
                            jPBD.setForeground(Color.BLACK);
                            jPBD.setText("OK ");
                        }
                    } else {
                        jPBD.setBackground(Color.RED);
                        jPBD.setForeground(Color.WHITE);
                        jPBD.setText(" ER ");
                    }


                    if (autoRec){
                        jbtnConnect.setText("Auto");
                        if (Start && Connected) {
                            jbtnConnect.setForeground(Color.BLACK);
                            jbtnConnect.setBackground(Color.GREEN);
                        } else {       
                            jbtnConnect.setForeground(Color.WHITE);
                            jbtnConnect.setBackground(Color.RED);
                        }
                    } else {
                        if (Start) {
                            jbtnConnect.setText("Disconnect");
                            jbtnConnect.setForeground(Color.BLACK);
                            jbtnConnect.setBackground(Color.GREEN);

                        } else {
                            jbtnConnect.setText("Connect");
                            jbtnConnect.setForeground(Color.WHITE);
                            jbtnConnect.setBackground(Color.RED);
                        }
                    }

                    jtfErroriCitire.setText(String.valueOf(totalErrors));

                    try
                    {
                        Thread.sleep(300);
                        jPanel4.repaint(1);
                    }
                    catch (InterruptedException ex)
                    {
                        Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
                    }
                }
                catch (Exception ex) {
                    Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
        });
        updateUIThread.start();
    }

並且還有其他像以上這樣啟動的線程,在這里我得到了不同的值,這些值在上述線程中進行了更新。

我的問題是為什么我的代碼不會對從另一個線程更新的UI元素引發任何異常? 我沒有使用SwingUtilities.invokeLater(new Runnable() { //code here }); 而且我的代碼執行得很好...

謝謝!

擺動不是線程安全的,而是單線程的。 同樣,您永遠都不要從事件調度線程外部更新UI組件,同樣,您也絕不應該在EDT中運行長時間運行的進程或阻塞代碼,因為這將阻止它處理事件隊列中的新事件,從而使您的應用看起來像是掛...因為它有...

有關更多詳細信息,請參閱Swing中的並發

我意識到,撓了一下頭之后,簡單的解決方案是只使用javax.swing.Timer

您想以固定的間隔(300毫秒)重復更新並更新UI,完美的是,Swing Timer能夠以固定的間隔安排更新並在EDT的上下文中執行回調!

它還具有合並重復呼叫的能力。 這意味着,如果事件隊列中已經存在“計時器”操作,則計時器將不會生成新的計時器,從而防止EDT泛濫並可能導致性能問題...

javax.swing.Timer timer = new Timer(300, new ActionListener() {
    public void actionPerformed(ActionEvent evt) {    
        jtfIP.setEnabled(!Start && !autoRec);
        jtfPort.setEnabled(!Start && !autoRec);
        jtfSlaveID.setEnabled(!Start && !autoRec);
        jtfTimeout.setEnabled(!Start && !autoRec);
        jtfReqInterval.setEnabled(!Start && !autoRec);
        jCheckBox1.setEnabled(!Start && !autoRec);
        jCBReconnect.setEnabled(!Start && !autoRec);

        if (db != null) {
            if (!db.getIsOpen()) {
                jPBD.setBackground(Color.RED);
                jPBD.setForeground(Color.WHITE);
                jPBD.setText("ER");
            } else {
                jPBD.setBackground(Color.GREEN);
                jPBD.setForeground(Color.BLACK);
                jPBD.setText("OK ");
            }
        } else {
            jPBD.setBackground(Color.RED);
            jPBD.setForeground(Color.WHITE);
            jPBD.setText(" ER ");
        }


        if (autoRec){
            jbtnConnect.setText("Auto");
            if (Start && Connected) {
                jbtnConnect.setForeground(Color.BLACK);
                jbtnConnect.setBackground(Color.GREEN);
            } else {       
                jbtnConnect.setForeground(Color.WHITE);
                jbtnConnect.setBackground(Color.RED);
            }
        } else {
            if (Start) {
                jbtnConnect.setText("Disconnect");
                jbtnConnect.setForeground(Color.BLACK);
                jbtnConnect.setBackground(Color.GREEN);

            } else {
                jbtnConnect.setText("Connect");
                jbtnConnect.setForeground(Color.WHITE);
                jbtnConnect.setBackground(Color.RED);
            }
        }

        jtfErroriCitire.setText(String.valueOf(totalErrors));
    }
});
timer.start();

有關更多詳細信息,請參見如何使用Swing計時器

Swing表示您不應從Swing Event Dispatch Thread外部更新組件,但不會強制這樣做。 檢查每個調用來自哪個線程根本不可行。

另外,由於線程問題(通常是線程問題)導致的問題的性質,您不應該期望在多線程代碼中出現錯誤時總是拋出異常。 這是因為線程問題經常導致死鎖內存一致性錯誤 ,這些錯誤在大多數情況下是不可恢復的(通常是整個JVM崩潰)。

而我做到了。

private void updateUI() {
    updateUIThread = new Thread(() ->
    { 
        while (true) {
            try {
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        jtfIP.setEnabled(!Start && !autoRec);
                        jtfPort.setEnabled(!Start && !autoRec);
                        jtfSlaveID.setEnabled(!Start && !autoRec);
                        jtfTimeout.setEnabled(!Start && !autoRec);
                        jtfReqInterval.setEnabled(!Start && !autoRec);
                        jCheckBox1.setEnabled(!Start && !autoRec);
                        jCBReconnect.setEnabled(!Start && !autoRec);

                        if (db != null) {
                            if (!db.getIsOpen()) {
                                jPBD.setBackground(Color.RED);
                                jPBD.setForeground(Color.WHITE);
                                jPBD.setText("ER");
                            } else {
                                jPBD.setBackground(Color.GREEN);
                                jPBD.setForeground(Color.BLACK);
                                jPBD.setText("OK ");
                            }
                        } else {
                            jPBD.setBackground(Color.RED);
                            jPBD.setForeground(Color.WHITE);
                            jPBD.setText(" ER ");
                        }


                        if (autoRec){
                            jbtnConnect.setText("Auto");
                            if (Start && Connected) {
                                jbtnConnect.setForeground(Color.BLACK);
                                jbtnConnect.setBackground(Color.GREEN);
                            } else {       
                                jbtnConnect.setForeground(Color.WHITE);
                                jbtnConnect.setBackground(Color.RED);
                            }
                        } else {
                            if (Start) {
                                jbtnConnect.setText("Disconnect");
                                jbtnConnect.setForeground(Color.BLACK);
                                jbtnConnect.setBackground(Color.GREEN);

                            } else {
                                jbtnConnect.setText("Connect");
                                jbtnConnect.setForeground(Color.WHITE);
                                jbtnConnect.setBackground(Color.RED);
                            }
                        }

                        jtfErroriCitire.setText(String.valueOf(totalErrors));
                    }
                });
                try
                {
                    Thread.sleep(300);
                    jPanel4.repaint(1);
                }
                catch (InterruptedException ex)
                {
                    Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
                }
            }
            catch (Exception ex) {
                Logger.getLogger(MainForm.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    });
    updateUIThread.start();
}

我將我的更新UI代碼放在invokeLater的run方法中。 updateUI()在應用程序啟動時調用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM