java - 事件派发线程在哪里调用?

标签 java multithreading swing event-dispatch-thread

我读到所有构造 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 是否会启动 EDT 以及 main 方法?)
  2. 按钮的事件处理程序代码是否在 EDT 上运行?

最佳答案

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

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

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

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

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

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

void executeOffEDT() {
  if (SwingUtilities.isEventDispatchThread()) {
    Runnable r = new Runnable() {
      @Override
      public void run() {
        OutsideClass.this.executeOffEDTInternal();
      }
    };
    new Thread(r).start();
  } else {
    this.executeOffEDTInternal();
  }
}

void executeOnEDT() {
  if (SwingUtilities.isEventDispatchThread()) {
    this.executeOnEDTInternal();
  } else {
    Runnable r = new Runnable() {
      @Override
      public void run() {
        OutsideClass.this.executeOnEDTInternal();
      }
    };
    SwingUtilities.invokeLater(r);
  }
}

关于java - 事件派发线程在哪里调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3636591/

相关文章:

java - 我们如何在android中为默认键盘添加贴纸

java - 如何在 Twitter Finagle 中禁用缓存?

java - 从子类调用 awt Frame 方法

c# - Azure Functions 使用带有 Http 触发器的取消 token

java - 使用 servlet 显示图像

java - MyBatis 在一个查询中删除-插入-更新

wpf - D3DImage 和 SharpDX 在慢速硬件上闪烁

c - OpenSSL C 多线程客户端段错误

java - JScrollPane 中的 JTable 在 Mac OS X 中无法正确显示

java - 在游戏中使用图形的最佳方式