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

inf*_*ous 1 java swing multithreading 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!");
      }
    });
  }
} ///:~
Run Code Online (Sandbox Code Playgroud)

然后通过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);
  }
} ///:~
Run Code Online (Sandbox Code Playgroud)

其中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);
      }
    });
  }
} ///:~
Run Code Online (Sandbox Code Playgroud)

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

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

JB *_*zet 5

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

javadoc引用:

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

(强调我的)