Java 中的主线程与 UI 线程

Posted

技术标签:

【中文标题】Java 中的主线程与 UI 线程【英文标题】:Main Thread vs. UI Thread in Java 【发布时间】:2011-11-01 16:27:28 【问题描述】:

在此处作为答案给出的许多 Swing sn-ps 中,有一个来自 main 方法的对 SwingUtilities#invokeLater 的调用:

public class MyOneClassUiApp 

    private constructUi() 
        // Some Ui related Code
    

    public static void main(String[] args) 
        SwingUtilities.invokeLater(new Runnable() 
            public void run() 
                new MyOneClassUiApp().constructUi();
            
        );
    

但是根据Threads and Swing article,从主线程构造UI是安全的:

一些方法是线程安全的:在 Swing API 文档中, 线程安全方法标有以下文本:

这个方法是线程安全的,虽然大多数 Swing 方法不是。

应用程序的 GUI 通常可以构建并显示在主界面中 thread:下面的典型代码是安全的,只要没有组件 (摇摆或其他)已实现:

public class MyApplication 
public static void main(String[] args) 
   JFrame f = new JFrame("Labels");
   // Add components to 
   // the frame here... 
   f.pack(); 
   f.show(); 
   // Don't do any more GUI work here... 
    

那么,通过SwingUtilities#invokeLater 在 main 中构造 UI 是否有真正的(线程安全)理由,或者这只是一种习惯,要记住在其他情况下这样做?

【问题讨论】:

是的,你必须在所有情况下都包装 main 方法,是的,大多数 JComponents 都是线程安全的,但对于它们的嵌套和继承方法无效,仅此而已,抱歉,也许这个 . 另见Q&A,它检查“为什么 GUI 是单线程的?” 【参考方案1】:

Swing 单线程规则:Swing 组件和模型应该从事件调度线程中创建、修改和查询。”—Java Concurrency in Practice ,还讨论了here 和here。如果您遵守此规则,则您无法可靠地构造、修改或查询任何可能假定您确实遵守该规则的组件或模型。一个程序可能看起来正常工作,但在不同的环境中却神秘地失败了。由于违规行为可能不明显,请使用here 提到的方法之一验证正确使用。

【讨论】:

【参考方案2】:

我认为使用SwingUtiltities.invokeLater() 只是一种更简单的异步执行代码的方法。有时某些应用程序需要它:例如,您可以同时创建 2 个单独的窗口。而已。

【讨论】:

这不仅仅是一种异步执行某些代码的方法。它在 Event Dispatcher Thread 中执行给定的Runnable,并且根据情况可能会有所不同 @Luismahou,谢谢。我不知道这个区别。在事件调度线程中执行代码有什么好处?我只知道缺点:如果任务由于某种原因需要很长时间,它可能会阻止其他事件的调度。 一般来说 Swing 相关的代码应该在 EDT 中执行。为什么?仅仅是因为 Swing 不支持多线程。例如,如果您在与 EDT 不同的线程中更改 JLabel 的文本,而 EDT 正在执行同一 JLabel 的 paint() 方法,则可能会发生意外情况。关于缺点,SwingWorker是官方的解决方案,有助于在EDT之外执行非UI代码。 注意:JLabel.setText 不是一个很好的例子,因为它默认在 EDT 中运行(以及其他一些)。但如有疑问,请在 EDT 中运行。【参考方案3】:

在 main 方法中创建 Swing UI 是安全的,因为在设置 UI 之前其他组件将如何显示?只要你还没有在屏幕上扔一些东西,你就没事了。换句话说,这会很糟糕:

public class MyApplication
 
  public static void main(String[] args)
   
    JFrame f = new JFrame("Labels"); 
    // Add components to  
    // the frame here...  
    f.pack();  
    f.show();  

    // now make another frame:
    JFrame f2 = new JFrame("Labels2"); 
    // Add components to the 2nd frame here...  
    f2.pack();  
    f2.show();  
    

如果您执行了上述操作,您将启动并运行 JFrame f,然后您将在事件调度线程 (EDT) 之外添加 Swing UI 组件。 invokeLater 在 EDT 上运行代码 - 如果您想更加安心,使用它不会有什么坏处。

【讨论】:

关于您的示例 - 它非常接近我给出的第二个示例(来自 Java 的站点)。所以你说这是关于“安心”,对吧? 是的,因为那时(只需输入main)屏幕上还能实现什么?没什么,因为你还没有做任何事情!除了那种非常特殊的情况,您必须非常小心,只在 EDT 上执行 Swing 代码。就个人而言,我从事 Swing 编程多年,从未遇到过直接从 main 创建 UI 的问题。 我只玩了几年的 Swing,但我也从来没有遇到过问题。我想我没有运行复杂的 UI 应用程序......

以上是关于Java 中的主线程与 UI 线程的主要内容,如果未能解决你的问题,请参考以下文章

当 Cocoa 应用程序中的主线程被阻塞时,UI 不会更新

在主线程上处理大型全局对象时如何不阻止来自工作线程的主 UI 线程

QT中UI主窗口如何与子线程相互传递参数

11.1-全栈Java笔记:多线程技术的基本概念

Android ActivityThread(主线程或UI线程)简介

从Java线程到kotlin协程之多线程的基本概念