如何在java swing中以线程安全的方式初始化gui对象?

时间:2014-11-09 22:30:09

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

我正在阅读Thinking in Java,作者强调主要方法不应该调用swing方法。作为这种做法的一个例子,他提出了以下代码(可在他的网页上找到):

//: gui/SubmitSwingProgram.java
import javax.swing.*;
import java.util.concurrent.*;

public class SubmitSwingProgram extends JFrame {
  JLabel label;
  public SubmitSwingProgram() {
    super("Hello Swing");
    label = new JLabel("A Label");
    add(label);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setSize(300, 100);
    setVisible(true);
  }
  static SubmitSwingProgram ssp;
  public static void main(String[] args) throws Exception {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() { ssp = new SubmitSwingProgram(); }
    });
    TimeUnit.SECONDS.sleep(1);
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        ssp.label.setText("Hey! This is Different!");
      }
    });
  }
} ///:~

然后通过invokeLater方法创建并初始化gui对象,使其成为线程安全的。但是几页之后,作者提出了以下代码:

//: gui/Button2.java
// Responding to button presses.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import static net.mindview.util.SwingConsole.*;

public class Button2 extends JFrame {
  private JButton
    b1 = new JButton("Button 1"),
    b2 = new JButton("Button 2");
  private JTextField txt = new JTextField(10);
  class ButtonListener implements ActionListener {
    public void actionPerformed(ActionEvent e) {
      String name = ((JButton)e.getSource()).getText();
      txt.setText(name);
    }
  }
  private ButtonListener bl = new ButtonListener();
  public Button2() {
    b1.addActionListener(bl);
    b2.addActionListener(bl);
    setLayout(new FlowLayout());
    add(b1);
    add(b2);
    add(txt);
  }
  public static void main(String[] args) {
    run(new Button2(), 200, 150);
  }
} ///:~

其中SwingConsole是:

//: net/mindview/util/SwingConsole.java
// Tool for running Swing demos from the
// console, both applets and JFrames.
package net.mindview.util;
import javax.swing.*;

public class SwingConsole {
  public static void
  run(final JFrame f, final int width, final int height) {
    SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        f.setTitle(f.getClass().getSimpleName());
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setSize(width, height);
        f.setVisible(true);
      }
    });
  }
} ///:~

因此,与前面的示例相反,在主方法/主线程中创建并初始化实现JFrame的对象。

我的问题是: (1)第二个例子是错误的还是第一个被夸大了? (2)仅在setVisible调用之后通过invokeLater调用swing方法并且在该语句之前调用主线程中的swing方法是否安全是否足够?

2 个答案:

答案 0 :(得分:5)

第二个例子是错误的。必须从事件派发线程创建和使用Swing组件。请参阅https://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html

引自the javadoc

  

不会在事件派发线程上调用对应用程序的主要方法或Applet中的方法的调用。因此,在构建和显示应用程序或小程序时,必须注意将控制权转移到事件派发线程。

(强调我的)

答案 1 :(得分:-1)

使用main()方法的示例可能有点误导。规则AFAIK是只允许主线程执行Swing更新/重绘等,每个definitionem 的main()方法是主线程。所有其他线程都需要通过SwingUtilities.invokeLater(...)来安排内容。即使对于主线程,它也可能会改善延迟,因为您可能会在主线程中花费大量时间,并且只要主线程不太忙,就会调度GUI刷新敌人。 编辑:证明错误