【问题标题】:How to initialize gui objects in a thread safe manner in java swing?java - 如何在java swing中以线程安全的方式初始化gui对象?
【发布时间】:2014-11-09 22:30:08
【问题描述】:

我正在阅读 Thinking in Java,作者强调 main 方法不应该调用 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 方法是安全的吗?

【问题讨论】:

  • 他可能会争辩说,在框架被打包或可见(也就是实现)之前,它可以安全处理。这种想法的问题是,您只是不知道什么会触发事件队列的请求。在 EDT 的上下文中初始化 UI 总是更安全

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


【解决方案1】:

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

引用the javadoc

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

(强调我的)

【讨论】:

  • 是的。尽管run() 是对调用适当SwingUtilities 方法的方法的调用,但实际构造(对new 的调用)是在主线程上调用的。不好!
  • 感谢您的出色回答,链接说明了一切。不过我很惊讶,我认为 Thinking in Java 被认为是学习这门语言的更好资源之一。
【解决方案2】:

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

【讨论】:

  • 我认为您的术语令人困惑,通常主线程被认为是启动程序的线程(调用 main),而 Swing 使用不同的线程,称为事件调度线程,其中所有 UI 代码应该从执行
猜你喜欢
  • 1970-01-01
  • 2014-07-11
  • 1970-01-01
  • 1970-01-01
  • 2010-10-09
  • 2010-10-27
  • 1970-01-01
  • 1970-01-01
  • 2018-08-05
相关资源
最近更新 更多