【问题标题】:How does ignoring the event dispatch thread allow this program to work?忽略事件调度线程如何让这个程序工作?
【发布时间】:2015-05-08 19:28:11
【问题描述】:

我试着看看我是否可以回答this question earlier today。我意识到我并不完全理解 Event Dispatch Thread (EDT)。 Googling 确认并帮助解决了这一点,并澄清了 why I don't。 (This 也可能与理解有关。)

代码设置了一个 GUI,然后(如前面的问题)更新一个文本字段,直到取消设置一个标志。

我有几个问题/要求。

    1234563在 EDT 上执行(是吗?)。这不是招致失败吗?
  • 如果对swingInit 的调用在内部并且doItinvokeLater 外部,则代码也会运行。所以swingInit 在 EDT 上执行,但 doIt 不在 EDT 上执行不应该是个问题吗? (我很惊讶这行得通。我应该是吗?)

  • 我想我明白为什么如果 doItinvokeLater 内,不管 swingInit 在哪里,它为什么会挂起:invokeLater 的目的只是初始化 GUI(对吧?)。

  • doIt 是否应该只在 EDT 上启动(可能从发生的事件),但肯定invokeLater 块内启动?

(EDT 概念的历史很有趣。并非总是如此。请参阅上面的“为什么我不理解它”的链接。)

import static java.awt.EventQueue.invokeLater;
import java.awt.event.*;
import javax.swing.*;

public class Whatever 
{
  static boolean flag = true;
  static JTextField tf = new JTextField("Hi",20);
  static JPanel p = new JPanel();
  static JFrame f = new JFrame();
  static JButton b = new JButton("End");

  public static void main(String[] args)
  {
    swingInit();

    invokeLater
    (
      new Runnable()
      {
        @Override
        public void run() 
        {
    //    swingInit();
    //    doIt();
        }
      }
    ); 
   doIt();      
  } 

  static void swingInit()
  {
     b.addMouseListener
    (
        new MouseAdapter()
        {
          @Override
          public void mouseClicked(MouseEvent e)
          {
            flag = false;
            JOptionPane.showMessageDialog(null,"Clicked... exiting");
            System.exit(0);
          }
        }
    );

    p.add(tf);
    p.add(b);
    f.add(p);
    f.setVisible(true);
    f.pack();
    f.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
  }

  static String getInfo(){
    return "Hello... " + Math.random();
  }

  static void doIt(){
    while(flag)     
      tf.setText(getInfo());
  }; 
}

【问题讨论】:

  • 我认为您理解,您不应该从 EDT 以外的线程更新 GUI - 但是,这样做不会必然失败 - 只是非常冒险。因此,如果此类代码有时似乎有效,请不要感到惊讶。

标签: java event-dispatch-thread invokelater


【解决方案1】:

考虑每个要点:

  • 代码在主线程上启动 - EDT 并行运行。 swingInit 在构建 UI 之后返回 控制 EDT,允许 dotIt 在主线程上并行执行其操作

  • 与上述情况类似,但在这里您要保证在 EDT 上构建 UI(如 Oracle 建议的那样)。

  • 一个长时间运行的任务被放置在 EDT 上,阻止它显示(如果放置在 swingIt 之前)或绘画和交互(如果放置在之后)。 the purpose of invokeLater is ONLY to initialize the GUI 目的是将非线程安全的 Swing 调用放到 EDT 上。如果在 main 方法中,我建议使用SwingUtilities.invokeAndWait

  • 如果您希望像这样更新 UI,请考虑使用 SwingTimer

在 EDT 之外运行特定于 EDT 的非线程安全代码不保证失败,但它确实会导致失败(通过两个(或更多)线程时的冲突尝试同时更新数据)。

我曾经花费数小时追踪一个神秘的NullPointerException,却发现这是一个LookAndFeel 问题,其呼叫不在美国东部时间。学过的知识。

【讨论】:

  • @copeg--感谢您的详尽回答。只有一个问题:“保证在 EDT 上构建 UI(良好实践)”不应该在 EDT 上说“..线程安全)"?
  • 不客气 - 我希望它能为您澄清问题...根据您的建议编辑答案
【解决方案2】:
static void doIt(){
  while(flag)     
    tf.setText(getInfo());
}; 

doIt 内部的繁忙循环占用了 GUI 线程(在该循环中旋转),这将导致 GUI 挂起。

您实际上并没有解释“运行良好”是什么意思,但我假设这就是您所看到的问题。

您可能希望使用Swing Timer 来执行您在循环中执行的操作。

【讨论】:

  • 谢谢。 “运行良好”意味着文本字段“按预期”不断更新,结果可见变化,直到单击“结束”按钮。如果你想看看会发生什么,代码应该按原样运行。
猜你喜欢
  • 2011-01-29
  • 1970-01-01
  • 2012-06-11
  • 1970-01-01
  • 1970-01-01
  • 2011-12-29
  • 2011-11-11
  • 2013-09-16
  • 1970-01-01
相关资源
最近更新 更多