在主方法中为什么要使用SwingUtilities.invokeLater?

23

经过多年的Java编程,我总是像这样创建我的main()方法:

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

但最近我研究了一些网络代码,发现有时候会使用以下内容代替通常的main()

public static void main(String[] args) 
{
    SwingUtilities.invokeLater(new Runnable() 
    {
        public void run() 
        {
            runProgram();
        }
    });
}

我只是想知道:

  • 为什么要使用这种方法而不是通常的main()方法?当我尝试时,我看不出任何区别。
  • 这两种方法有什么区别?

谢谢您的阅读和回答。

4个回答

22

文档解释如下。取自初始线程

为什么初始线程不直接创建GUI呢?因为几乎所有创建或与Swing组件交互的代码都必须在事件分派线程上运行。

并且从事件分派线程

在API规范中,一些Swing组件方法被标记为“线程安全”,可以从任何线程安全地调用它们。所有其他Swing组件方法必须从事件分派线程中调用。忽略此规则的程序可能会大部分时间正常运行,但容易遇到难以复现的不可预测的错误。


我明白了,非常有趣。感谢你提供的链接和突出的要点。 - Rob
有没有一个小而明确的例子,说明如果main直接调用runProgram()会出现什么问题?就我所看到的,这里提供的链接和摘录似乎都没有回答这个问题。 - Don Hatch

13
因为被虚拟机启动的线程“main”不是“事件分派线程”。

3

API中的一些Swing组件不是线程安全的,这意味着它们可能会导致死锁等问题。因此,最好使用Swing提供的事件分发线程来创建和更新这些Swing组件,而不是从主线程或任何其他从主线程创建的线程中进行操作。


1

虽然上面的答案都是正确的,但我认为它们缺乏正确的解释。

是的,与Swing交互的所有内容(创建UI、更新UI、添加新组件或布局等)都应始终在AWT事件分派线程上完成(有关该主题的更多信息,请参见this post)。

SwingUtilities.invokeLater()将您的代码放置在事件分派线程(EDT)的FIFO队列中,因此只有在EDT完成其他任务后才会从EDT执行它。


话虽如此,EDT 应该仅用于运行快速执行的 Swing 相关任务(如果阻塞 EDT,则会阻塞整个 UI)。

如果您没有使用 Swing/AWT(例如 JavaFX 应用程序或终端应用程序),则在主方法上使用 SwingUtilities.invokeLater() 没有意义。

如果您想执行一些与 Swing 完全无关但需要启动 Swing 的任务(例如,在类似 MVC 的应用程序中启动模型和控制器),则可以从 EDT 或主线程中执行它们(有关此主题的讨论,请参见 this post )。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接