【问题标题】:What happens when Swing's threading policy is violated?当 Swing 的线程策略被违反时会发生什么?
【发布时间】:2010-11-28 14:49:41
【问题描述】:

在过去的几年中,我主要在 Eclipse 中完成 UI 开发,这在线程访问方面非常保守:任何从 UI 线程外部更改 UI 小部件属性(例如颜色、文本)的尝试抛出异常。

我现在正在查看一个现有的 Swing 程序,它有一个带有大量自定义小部件的窗口。有一个单独的线程为这些小部件中的每一个运行一个突变函数,突变函数读取一些东西的值(例如,标签颜色和值)并写入一些(例如,更改背景颜色)。请注意,没有涉及自定义绘画或类似的东西,只是对其包含的子小部件进行了一堆更改,这些小部件主要是 JLabel。

目前,它从单独的线程运行,而不是从 Swing 事件线程运行。该线程遍历所有 400 个小部件并在每个小部件上调用 mutator。更新似乎工作正常,但 GUI 对用户输入没有响应。

如果我从 Swing 线程外部运行大约 0.4 毫秒的整个过程,并将对 mutator 的每个调用包装在 invokeLater 或 invokeAndWait 中,那么 UI 的响应速度就会快得多。

我想了解的是:

1) 从 Swing 线程外部进行所有这些调用有时是否合法?

2) 对 Swing 线程有什么影响,为什么当我从外部调用它时 UI 响应较慢?

【问题讨论】:

    标签: java performance multithreading swing


    【解决方案1】:

    从“什么都没有”到间歇性问题再到“一切都崩溃了,让每个人都在 GUI 上工作!”

    主要(最明显的)视觉效果是,如果您按住 GUI 线程(例如有人按下按钮,而您执行 sleep(5000) 或其他操作),您的 GUI 将不会重新绘制。它不能,因为你抓住了它允许通过你的唯一线程!这让人们认为Java真的很慢。这还不错,但是编程很容易,很多不费心研究这种做法的人已经生产了运输产品。

    下一个最大的问题是,当您在另一个线程中绘制屏幕时(例如传递给 main 的线程),它可能会出现奇怪的行为。 Swing 对如何渲染帧已经太挑剔了——把线程作为变量取出来!

    最后,很少(或者经常如果您在错误线程上的紧密循环中调用 swing 组件)会发生线程冲突。如果发生这种情况,可能会抛出(或不抛出)异常,并且可能会出现错误,但这可能并不明显。

    【讨论】:

      【解决方案2】:

      1) 从 Swing 线程外部进行所有这些调用有时是否合法?

      有一些例外情况(例如,设置文本字段的文本值会为您自动进行 EDT 代理) - 但在 没有 的情况下最好这样做。如果您正在执行大量更新,则可以在单个 EDT 调用(对 invokeLater() 的单个调用)中完成所有更新,而不是单独调用 - 但即使是那种批处理也很少有帮助。长短:从 EDT 对 Swing 组件执行操作。这包括读取和写入。

      2) 对 Swing 线程有什么影响,为什么当我从外部调用它时 UI 响应较慢?

      嗯,EDT 负责更新 GUI。如果您从外部调用,它并不是“响应性较差”——而是不会发生(根本)更新用户界面的实际低级系统调用。您的应用程序中可能发生的情况是,最初的开发人员很幸运,并且在 Swing 组件中更改了状态,而没有创建真正令人讨厌的竞争条件。然后其他一些事件导致在 EDT 上发生重绘,从而导致组件被更新。这可能看起来是“缺乏响应能力” - 但真正发生的是“缺乏屏幕刷新”。

      EDT 只是一个常规线程,但它有点特殊,因为它在处理 GUI 相关信号(例如绘图命令)的紧密循环中运行。在 EDT 上发布这些类型的命令的语义与我们通常认为的 Java 线程(它涉及将操作提交到消息泵)确实非常不同。

      长短——所有那些说“只与 EDT 上的 Swing 对象交互”的 Javadocs 都是有原因的。不要惹它。如果您想进行后台处理,很好 - 但您负责将与 J* 组件的交互代理回 EDT(通常使用 invokeLater() )。

      【讨论】:

        【解决方案3】:
        1. 确实没有例外。 Kevin 部分正确 - JTextComponent.setText() 被宣传为线程安全的。但是,查看 1.6 代码,它提供了对文档对象的同步并且不使用 EDT。这很好,除非另一个摆动组件(或控制摆动组件的东西)正在侦听文档对象。省去担心它的麻烦,总是使用 EDT - 就像 Kevin 说的那样,真的没有其他情况(我知道)可以这样做。

        2. 不深入代码就很难说;行为未定义。如果您的后台任务长时间运行(> 几秒钟),您会看到相反的效果 - 使用 EDT 将使 UI 在您的任务运行时无响应。

        幸运的是,听起来正确的方法对你最有效。 :)

        Sun曾经说过用其他线程还没有实现的组件是可以的,但后来又放弃了:

        Related stackoverflow question

        查看 Sun 关于摇摆和并发的 UI 教程(我会发布链接,但这是我在 stackoverflow0 上的第一个答案。

        【讨论】:

          【解决方案4】:

          基本问题是非线程安全对象以多线程方式执行。即使是像阅读HashMap 这样简单的事情也可能陷入无限循环。因为 AWT 使用锁(很糟糕),所以您也可能会遇到死锁。但是,您可能会侥幸成功,尽管您可能会发现 Java 的更新版本突然导致您在某些客户计算机上出现问题。

          (顺便说一句:这是 AWT 的事件调度线程,而不是 Swing 的。)

          【讨论】:

            【解决方案5】:

            对于 (1),根据经验,任何更新屏幕像素的内容都必须从 Event Dispatching Thread (EDT) 调用。在某些 JVM 可以可接受地处理来自 EDT 外部的更新的情况下,您永远不应该依赖这种工作方式,某些机器和不同的外观将无法正常工作。行为未定义 - 这可以解释您所看到的缺乏响应能力。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2017-02-14
              • 2017-11-18
              • 1970-01-01
              • 1970-01-01
              • 2013-06-09
              • 1970-01-01
              相关资源
              最近更新 更多