9

我读到所有构建 Swing 组件和处理事件的代码都必须由事件调度线程运行。我了解这是如何通过使用该SwingUtilities.invokeLater()方法来完成的。考虑以下代码,其中 GUI 初始化是在main方法本身中完成的

public class GridBagLayoutTester extends JPanel implements ActionListener {   
    public GridBagLayoutTester() {
        setLayout(new GridBagLayout());
        GridBagConstraints gbc = new GridBagConstraints();

        JButton button = new JButton("Testing");
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.anchor = GridBagConstraints.WEST;
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = 1;
        button.addActionListener(this);
        add(button, gbc);
    }

    public void actionPerformed(ActionEvent e) {
        System.out.println("event handler code");
    }

    public static void main(String[] args) {
        JFrame frame = new JFrame("GridBagLayoutDemo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);    
        Container contentPane = frame.getContentPane();
        contentPane.setLayout(new BorderLayout());
        contentPane.add(new GridBagLayoutTester(), BorderLayout.CENTER);
        frame.setSize(800, 600);
        frame.pack();
        frame.setVisible(true);
        System.out.println("Exiting");
    }   
}

这段代码如何完美运行?我们正在JFrame主线程中构造和调用许多其他方法。我不明白 EDT 究竟在哪里出现(它正在执行什么代码?)。该类的构造函数GridBagLayoutTester也从该main方法中调用,这意味着 EDT 没有运行它。

简而言之

  1. EDT 什么时候开始?(如果在运行此代码时启动了 EDT,JVM 是否会与 main 方法一起启动 EDT?)
  2. 按钮的事件处理程序代码是否在 EDT 上运行?
4

4 回答 4

12

该代码完美运行,因为您在 EDT 有机会与之交互之前在主线程中构建框架。从技术上讲,您永远不应该这样做,但从技术上讲,在这种特定情况下您可以这样做,因为在 JFrame 变得可见之前您无法与它进行交互。

要知道的要点是 Swing 组件不是线程安全的。这意味着它们不能同时从多个线程进行修改。这可以通过确保所有修改都来自 EDT 来解决。

EDT 是专用于用户交互的线程。用户生成的任何事件始终在 EDT 上运行。任何用户界面更新都在 EDT 上运行。例如,当您调用 时Component.repaint(),您可以从任何线程调用它。这只是设置一个标志来将组件标记为需要油漆,EDT 会在其下一个周期执行此操作。

EDT 是自动启动的,并且与系统实现密切相关。它在 JVM 中得到了很好的处理。通常,它与窗口系统中处理用户交互的单个线程相关。当然,这完全取决于实现。好处是您不必担心这一点。您只需要知道 - 如果您与任何 Swing 组件交互,请在 EDT 上进行。

同样,还有另一件事很重要。如果您要对外部资源进行任何长时间的处理或阻塞,并且您要响应用户生成的事件,您必须安排它在 EDT 之外的自己的线程中运行。如果您不这样做,您将导致用户界面在等待长时间处理运行时阻塞。优秀的示例包括从文件加载、从数据库读取或与网络交互。您可以使用该方法测试您是否在 EDT 上(对于创建可以从任何线程调用的中性方法很有用)SwingUtilities.isEventDispatchThread()

以下是我在编写处理 EDT 的 Swing 编程时经常使用的两个代码片段:

无效 executeOffEDT() {
  如果(SwingUtilities.isEventDispatchThread()){
    可运行 r = new Runnable() {
      @覆盖
      公共无效运行(){
        OutsideClass.this.executeOffEDTInternal();
      }
    };
    新线程(r).start();
  } 别的 {
    this.executeOffEDTInternal();
  }
}

无效 executeOnEDT() {
  如果(SwingUtilities.isEventDispatchThread()){
    this.executeOnEDTInternal();
  } 别的 {
    可运行 r = new Runnable() {
      @覆盖
      公共无效运行(){
        OutsideClass.this.executeOnEDTInternal();
      }
    };
    SwingUtilities.invokeLater(r);
  }
}
于 2010-09-03T14:19:07.157 回答
3

事件调度线程,顾名思义,在每次需要处理事件时由 Swing 调用。

在您给出的示例中,“测试”按钮将在需要处理操作事件时自动调用 actionPerformed 方法。因此,您的 actionPerformed 方法的内容将由 Event Dispatch Thread 调用。

要回答您的最后两个问题:

  • 当 Swing 框架加载时,EDT 会自动启动。您不必关心启动此线程,JRE 会为您处理此任务。
  • 事件处理程序代码由 EDT 运行。您的 Swing 界面生成的所有事件都被汇集在一起​​,并由 EDT 负责执行它们。
于 2010-09-03T14:18:53.210 回答
1

1)我不知道是 innew JFrame还是 insetVisible但它按需初始化,这就是 main 方法的结尾(在主进程线程上)不会终止进程。EDT 已启动并处于循环阻塞等待下一个事件。

2) 明确的。该循环从操作系统接收事件,找到 JButton 并告诉它事件已被触发。然后按钮调用监听器。所有这些都发生在 EDT 中。

您可以查看当您想终止进程(或关闭主窗口)时调用的 Swing 代码,以查看 EDT 的终止位置......这可以为您提供线索(我稍后会这样做!:)

于 2010-09-03T14:18:27.053 回答
0

setVisible(true);如果 EDT 线程尚未启动,则在您首先调用它之后启动 EDT 线程,ofc。或者,如果您调用SwingUtilities.invokeAndWait()SwingUtilities.invokeLater()方法。

http://www.leepoint.net/JavaBasics/gui/gui-commentary/guicom-main-thread.html

于 2013-07-04T22:32:31.073 回答