java从另一个线程更新UI组件 [英] java updating UI components from another thread

查看:151
本文介绍了java从另一个线程更新UI组件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我找到了很多关于我的问题的答案,但我仍然不明白为什么我的申请不会引起任何例外。
我在NetBeans 8中创建了一个新的java表单应用程序。
我的表单创建并在main方法中显示如下:

I found many answers about my question, but I still don't understand why my application does not throw any exceptions. I created a new java form application in NetBeans 8. My form is created and displayed in main method like this:

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并将其设置为可见。

So, this new Runnable creates new MainForm and sets it visible.

然后,在我的代码中,我启动了新的线程来更新一些jButtons和jTextFields。代码如下:

Then, in my code I start new threads which updates some jButtons and jTextFields. Code below:

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

还有其他线程如上所述启动,我得到不同的值在上面的主题中更新。

And there are other threads started like this above and where I get different values which are updated in the above thread.

我的问题是为什么我的代码不会抛出任何关于从另一个线程更新的UI元素的异常?我没有使用 SwingUtilities.invokeLater(new Runnable(){// code here});
我的代码执行完美......

My question is why my code does not throw any exception regarding UI elements which are updated from another thread? I did NOT use SwingUtilities.invokeLater(new Runnable() { //code here }); And my code executes perfectly...

谢谢!

推荐答案

Swing不是线程安全的,而且是单线程的。您永远不应该从事件调度线程外部更新UI组件,同样,您不应该在EDT中运行长时间运行的进程或阻止代码,因为这会阻止它处理事件队列中的新事件,从而导致您的应用看起来像它挂...因为它有......

Swing is not thread safe and is single threaded. You should never update UI components from outside the Event Dispatching Thread, equally, you should never run long running processes or blocking code within the EDT, as this will prevent it from processing new events within the event queue, causing your app to look like it's hung...because it has...

看看 Swing中的并发更多详细信息。

我抓了一会儿后,我意识到,简单的解决方案就是只需使用 javax.swing.Timer

After scratching my head for a while, I realised, the simple solution would be to just use javax.swing.Timer

您希望定期重复更新(300毫秒) )并更新用户界面,完美,Swing 计时器能够定期安排更新,并在EDT的上下文中执行回调!

You want to repeat the update at a regular interval (300 milliseconds) and update the UI, perfect, the Swing Timer is capable of scheduling updates at regular intervals and executes it call back within the context of the EDT!

它还具有合并重复呼叫的能力。这意味着,如果事件队列中已经存在计时器操作,则计时器将不会生成新的计时器,从而防止充斥EDT并导致可能的性能问题......

It also has the ability to consolidate repeated calls. This means, if there is already a "timer" action in the event queue, the timer will not generate a new one, preventing from flooding the EDT and cause possible performance issues...

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 Timers 获取更多详细信息

这篇关于java从另一个线程更新UI组件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆