在edt中更新swing组件

时间:2015-02-21 14:46:08

标签: java multithreading swing actionlistener event-dispatch-thread

如果我理解正确,那么当我创建GUI swing组件时,例如我有:

public class frameExample extends JFrame{
    public frameExample(){
    //Here adding bunch if components
    setVisible(true);
    }
}

因此,只要我不调用setVisible方法,就会从创建实例的线程中创建组件。因此,如果在我的主要方法的类中写:

JFrame test=new frameExample();

和我sysout

Thread.currentThread.getName(); 
在setExample的构造函数中的

就在setVisible之前我应该​​得到:main。

之后,创建和维护swing元素的责任被传递给event-dispatch-thread,因为它不是线程安全的,所以每个组件的添加/删除/修改都应该在EDT线程中完成。

所以我应该将setVisible作为构造函数中的最后一行代码,或者单独调用它。

据我所知,所有事件都是通过EDT进行的。因此,如果我在例如actionPerformed方法中创建一个新组件,它应该可以。

此外,如果我将runnable实例传递给invokeLater或invokeAndWait,那么所有run()方法都将由EDT完成。

所以这就是为什么我感到困惑。

我制作了这段代码:

public class GUI extends JFrame {


JButton btn = new JButton("Change");
JMenuBar m = new JMenuBar();

public GUI() {
    super("Test");
    setSize(400, 400);
    setDefaultCloseOperation(EXIT_ON_CLOSE);

    m.add(new JMenu("menu"));
    add(m, BorderLayout.NORTH);

    add(btn, BorderLayout.SOUTH);
    System.out.println("Current thread: before setVisible "+Thread.currentThread().getName());

    setVisible(true);

    System.out.println("Current thread: after setVisible "+Thread.currentThread().getName());
    btn.addActionListener(new ActionListener() {

        @Override
        public void actionPerformed(ActionEvent e) {

            add(new JButton("testbtn1"), BorderLayout.EAST);
            add(new JButton("testbtn2"));
            System.out.println("Current thread: "+Thread.currentThread().getName());


            new Thread(new Runnable() {

                @Override
                public void run() {
                    // TODO Auto-generated method stub
                    for (int i = 0; i < 1E8; i++) {
                        Math.sin(5.0);
                    }
                    System.out.println("Current thread: "+Thread.currentThread().getName());
                }
            }).start();

        }
    });
}

}

所以在我的anonim类中我在EDT中添加了两个按钮,但是在我调整它之后组件没有添加到我的框架中(这迫使edt更新其组件???这是什么原因此?)。

所以即使在edt中我也不会得到我的新组件,但是当我在edt之外创建一个随机线程并使其更改一些gui元素属性(例如setText)时,在edt之外工作正常。

所以我的第一个问题是:为什么我的组件没有在edt中更新,以及为什么它们在调整大小后可见

第二个问题:为什么我可以对edt外部的摆动组件进行更改,一切正常?这只是一个随机的线程行为,例如在没有同步块的情况下工作正常但是当你重新运行程序时,它会因为缺乏同步而最终崩溃。

1 个答案:

答案 0 :(得分:4)

  

因此,只要我不调用setVisible方法,就会从创建实例的线程中创建组件

错误。只要您没有专门创建新的Thread或使用实用程序方法(例如SwingUtilities#invoke...方法),就会在当前Thread上调用每个调用,包括{{ 1}}来电。

看起来您认为以某种方式使Swing组件可见会使您的代码切换线程。不,Swing组件的绘画将在EDT上进行。正如您所说,Swing不是线程安全的。 这就是为什么你应该在EDT上创建组件,而不是在另一个线程上。在大多数情况下它可能没有问题,但最终你会偶然发现奇怪的错误。

  

据我所知,所有事件都是通过EDT进行的。因此,如果我在例如actionPerformed方法中创建一个新组件,它应该可以。

     

此外,如果我将runnable实例传递给invokeLater或invokeAndWait,那么所有run()方法都将由EDT完成。

正确。

  

所以在我的anonim类中,我在EDT中添加了两个按钮,但是在调整大小之后组件没有添加到我的框架中

setVisible添加组件需要您重新验证布局(请参阅Container方法的javadoc)。只需致电

Container#add

添加按钮后,它将按预期工作。手动调整框架大小与您已经注意到的效果相同。

  

所以即使在edt中我也不会得到我的新组件,但是当我在edt之外创建一个随机线程并使其更改一些gui元素属性(例如setText)时,在edt之外工作正常。

如前所述,它可能在大多数时间都有效,但不能保证它会100%的时间都能正常工作。上面解释了添加组件无法按预期工作的原因,并且与线程问题无关。