Java Swing - 在 EDT 上运行

IT小君   2021-12-08T04:29:24

我有几个关于 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被调用?

感谢您的所有回答。

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

  2. 用户触发的一切都在 EDT 上处理。例如,如果用户点击一个按钮,actionPerformed相应的ActionorActionListener将在 EDT 上被调用。

  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);
         }
     }
    
2021-12-08T04:29:25   回复
IT小君
  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 上完成
2021-12-08T04:29:25   回复
IT小君

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 上运行

2021-12-08T04:29:25   回复
IT小君

1.在 Java GUI 应用程序中,main()方法的生命周期不长,在 中调度 GUI 的构建后 Event Dispatcher Thread,该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或使用SwingWorkerJava专门引入的来同步UI和Non-UI线程。

6. SwingWorker 不保证 done() 方法在 EDT 上运行,而是将它的输出同步到 EDT 线程,也就是 GUI 线程。

2021-12-08T04:29:25   回复