【问题标题】:Java Swing - running on EDTJava Swing - 在 EDT 上运行
【发布时间】:2012-08-06 09:03:49
【问题描述】:

我有几个关于 Swing 和使用 EDT 进行 GUI 更新的问题。我刚开始阅读这些东西,所以我是这个领域的初学者:

  1. 在 EDT 上运行需要哪些操作?如果他们不这样做,是否只是引发了一个异常?
  2. 是否有任何特定时间我们实际上是自动在 EDT 上的?
  3. 如果我们使用SwingUtilities.invokeLater 安排任务,我们会将其排入当前GUI 更新任务队列(EDT),对吗?
  4. 对上述队列的访问我猜是同步的,或者使用了一些并发集合,但是如果我调度两个GUI更新任务,从两个后台线程,是不可能说哪个会先添加?例如,如果线程 1 FIRST 提交了将 JLable 的文本设置为“是”的任务,然后不久之后,第二个线程出现并提交了将该值设置为“否”的任务,我们是否保证结果将是“是”,还是仅仅是操作系统如何安排这些事情的问题?
  5. SwingWorker 究竟如何确保 done() 方法在 EDT 上运行?它设置了以下代码:

      future = new FutureTask<T>(callable) {
                   @Override
                   protected void done() {
                       doneEDT();
                       setState(StateValue.DONE);
                   }
               };
    

所以我想知道 FutureTask 是否以某种方式确保调用了 invokeLater

感谢您的所有回答。

【问题讨论】:

    标签: java swing event-dispatch-thread


    【解决方案1】:
    1. 一个好的规则是所有操作(访问/更新/...)都应该在 EDT 上进行。 javadoc 中提到了一些例外(某些类的某些方法),但它们很难记住,因此更容易坚持“在 EDT 上做所有事情”的方法。不会引发异常(幸运的是,JavaFX 修复了这个缺点)。您可以使用自定义 RepaintManager 来检测大部分违规行为:请参阅 this article

    2. 用户触发的一切都在 EDT 上处理。例如,如果用户单击一个按钮,则在 EDT 上将调用对应的ActionActionListeneractionPerformed

    3. 正确

    4. 您首先安排的事情将首先执行。 invokeLater 调用只是在队列末尾添加 Runnable。稍后再次使用invokeLater 将在之前安排的Runnable 之后添加这个新的Runnable

    5. 看看doneEDT的代码

       private void doneEDT() {
           Runnable doDone = 
               new Runnable() {
                   public void run() {
                       done();
                   }
               };
           if (SwingUtilities.isEventDispatchThread()) {
               doDone.run();
           } else {
               doSubmit.add(doDone);
           }
       }
      

    【讨论】:

    • 两个问题: 4. 所以是第一个线程稍后执行invoke 对吧?因此,操作系统会安排第二个后台线程 invokeLater 调用 FIRST 并且顺序会混乱? 5. 我仍然不明白 EDT 是如何实现的。 do Submit 将 Runnable 添加到 AccumulativeRunnable...
    • @Bober02 查看 javax.swing.SwingWorker.DoSubmitAccumulativeRunnable 可运行,由于使用了 Swing Timer,它在 EDT 上执行。对于 4:是的,如果你想控制顺序,你必须确保你的一个后台线程在另一个线程调度它的 runnable 之前调度 runnable
    • 有时,提交给 doSubmit() 的所有内容都在 EDT 中运行。您不必关心它是如何以及在哪里完成的:这个类已经过测试,并且可以按照文档说明工作。
    • @JBNizet 为了我自己的理解和对 Swing 的更好理解,我想深入了解这一点。我现在看到了图片:在基类 - AccumulativeRunnable 中,从 add 调用的提交方法只是在 SwingUtilities 上使用了 invokeLater。这是我完全可以理解的。现在,submit 方法已被重写以安排一个不调用 SwingUtilities 的Timer。因此,在 EDT mnising 上调度任务的调用在哪里?
    • @Bober02 它是一个 Swing 计时器,它在 EDT 上调用它的 ActionListeners。该类的源代码显示了这一点
    【解决方案2】:
    1. 基本上,每次使用 Swing 组件或 Swing 组件的模型时,都必须在 EDT 中完成。如果您不这样做,则不会引发任何异常。它可以工作,但也不能工作,出现行为不稳定、数据损坏等。
    2. 在 EDT 中调用每个 Swing 事件侦听器。基本上,除了 main 方法,Swing 应用程序的每一行代码都默认在 EDT 中执行,除非您明确启动线程、使用 SwingWorker 或类似的东西。
    3. 是的。
    4. 提交给 SwingUtilities.invokeLater() 的任务按照提交的顺序执行。
    5. 在内部,它使用 SwingUtilities.invokeLater() 或类似方法。 FutureTask 与 Swing 没有任何关系。 SwingWorker 确保其 done 方法在 EDT 中执行。 doneEDT() 方法有以下注释:在 EDT 上完成调用

    【讨论】:

      【解决方案3】:

      SwingWorker 确保 done() 方法通过以下代码在 EDT 上运行:

          Runnable doDone =
              new Runnable() {
                  public void run() {
                      done();
                  }
              };
          if (SwingUtilities.isEventDispatchThread()) {
              doDone.run();
          } else {
              doSubmit.add(doDone);
          }
      

      其实就是把doDone变量加到AccumulativeRunnable doSubmit中,

      查看 AccumulativeRunnable.java 的源代码你会发现有如下代码

      受保护的无效提交(){

      SwingUtilities.invokeLater(this);

      }

      这就是为什么 swingworker 确保方法 done() 在 EDT 上运行

      【讨论】:

        【解决方案4】:

        1.在Java GUI应用程序中,main()方法寿命不长,Event Dispatcher Thread中调度GUI构建后,main()方法退出......现在它的 EDT 负责处理 GUI。

        2. 所以我们不需要在 EDT 上启动我们的应用程序,它会自动完成。

        3 始终保持 UI 在 UI 线程上工作,Non-UI 在 Non-UI 线程上工作。

        所以请始终保留您的 EDT 线程,它是仅用于 GUI 工作的 GUI 线程。

        例如:

        public static void main(String[] args){
            EventQueue.invokeLater(new Runnable(){
                  public void run(){    
                    myframe.setVisible(true);
                 }
             }
        }
        

        4. 创建一个单独的非 UI 线程来处理耗时的方法。

        5.您可以简单地使用Thread 或使用Java 中专门引入的SwingWorker 来同步UI 和Non-UI 线程。

        6. SwingWorker 不确保 done() 方法在 EDT 上运行,而是将其输出同步到 EDT 线程,即 GUI 线程。

        【讨论】:

        • 对于您的第 6 点:请参阅 Swingworker 的类 javadoc:事件调度线程:所有与 Swing 相关的活动都发生在此线程上。 SwingWorker 调用 process 和 done() 方法并通知此线程上的任何 PropertyChangeListeners。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-04-05
        • 2011-10-13
        • 2014-03-31
        • 2014-10-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多