在过去的几年里,我主要在Eclipse中完成UI开发,在线程访问方面非常保守:任何尝试从UI线程外部更改UI小部件上的属性(例如,颜色,文本)抛出异常。
我现在正在查看Swing中的现有程序,该程序具有包含大量自定义窗口小部件的窗口。存在针对这些小部件中的每一个运行突变功能的单独线程,并且突变功能读取某些事物的值(例如,标签颜色和值)并写入一些(例如,改变背景颜色)。请注意,没有涉及自定义绘画或类似的东西,只是对它包含的子窗口小部件的一些更改,主要是JLabel。
目前,这是从单独的线程运行,而不是从Swing事件线程运行。这个线程覆盖了所有400个小部件,并在每个小部件上调用mutator。更新似乎正常工作,但GUI没有响应用户输入。
如果我从Swing线程外部运行大约0.4毫秒的整个事件并将每个调用包装到invokeLater或invokeAndWait中的mutator,则UI的响应速度要快得多。
我想要了解的是:
1)从Swing线程外部进行所有这些调用有时是否合法?
2)对Swing线程有什么影响?当我从外面调用它时,为什么UI响应性较差?
答案 0 :(得分:3)
从“没什么”到间歇性问题再到“Everything Broke,让每个人都开始使用GUI!”
主要(最明显的)视觉效果是,如果你举起GUI线程(就像有人按下按钮而你做睡眠(5000)或其他什么),你的GUI将不会重新绘制。它不能,因为你抓住它允许通过你的唯一线程!这让人们认为Java真的很慢。这还不错,但很容易编程,许多不打扰此类研究实践的人已经制作了运输产品。
下一个最大的问题是当你在另一个线程中绘制屏幕时(比如传递给main的那个),它可能会有奇怪的行为。对于渲染帧的方式,Swing已经过于挑剔 - 将线程作为变量取出!
最后,很少(或者通常如果你在错误的线程上紧密循环中调用swing组件),你可能会遇到线程冲突。如果发生这种情况,可能会抛出(或不抛出)异常,并且可能会出现错误,但可能并不明显。
答案 1 :(得分:3)
1)从Swing线程外部进行所有这些调用有时是否合法?
有一些例外(设置文本字段的文本值,例如,为您自动执行EDT代理) - 但是有 no 情况下最好这样做。如果你正在进行大量的更新,你可以在一次EDT调用(一次调用invokeLater())而不是单独的调用中完成所有更新 - 但即使是那种批处理也很少有帮助。长和短:从EDT对Swing组件执行操作。这包括读写。
2)对Swing线程的影响是什么?当我从外面调用它时,为什么UI的响应性会降低?
好吧,EDT负责更新GUI。如果你从外面打电话,那就不是“反应迟钝” - 实际的低级别系统调用更新用户界面(根本不会)。您的应用程序可能发生的事情是,原始开发人员变得幸运并且在swing组件中改变状态而不会产生非常恶劣的竞争条件。然后一些其他事件导致在EDT上发生重绘,这导致组件被更新。这似乎是“缺乏响应能力” - 但真正发生的是“缺乏屏幕刷新”。
EDT只是一个常规线程,但它有点特别之处在于它在一个处理GUI相关信号(例如绘制命令)的紧密循环中运行。在EDT上发布这些类型的命令的赛门铁克与我们通常认为的Java线程(它涉及将操作提交到消息泵)真的有很大不同。
长短 - 所有那些说“只与EDT上的Swing对象互动”的Javadoc都是出于某种原因而编写的。不要乱用它。如果你想进行后台处理,那很好 - 但你有责任将与J *组件的交互代理回到EDT(最常用的是使用invokeLater())。
答案 2 :(得分:2)
确实没有例外。 Kevin部分正确 - JTextComponent.setText()被公布为线程安全。但是,查看1.6代码,它提供了文档对象的同步,并且不使用EDT。这很好,除非另一个swing组件(或控制swing组件的东西)正在监听文档对象。省去了担心它的麻烦,并且总是使用EDT - 就像Kevin说的那样,确实没有其他情况(我知道)不这样做。
很难说没有深入研究代码;行为未定义。如果您的后台任务长时间运行(>几秒钟),您会看到相反的效果 - 使用EDT会在您的任务运行时无法响应用户界面。
幸运的是,无论如何,这听起来像是以正确的方式做到最好。 :)
Sun曾经说过,可以将其他线程用于尚未实现的组件,但后来又被重新使用了:
Related stackoverflow question
查看Sun关于swing和并发的UI教程(我发布了链接,但这是我在stackoverflow0上的第一个答案。
答案 3 :(得分:1)
基本问题是非线程安全对象以多线程方式执行。甚至像阅读HashMap
这样简单的东西也可以被无限循环捕获。因为AWT使用锁(严重),你也可能最终导致死锁。但是,您可能会逃脱它,尽管您可能会发现Java的更新版本突然导致您在某些客户计算机上出现问题。
(顺便说一句:这是AWT的事件调度线程,而不是Swing的。)
答案 4 :(得分:1)
对于(1)作为经验法则,必须从Event Dispatching Thread (EDT)调用更新屏幕上像素的任何内容。如果某些JVM可以接受来自EDT外部的更新,那么你应该永远不要依赖于这种工作,一些机器和不同的外观将不会令人满意。行为未定义 - 这可以解释您所看到的缺乏响应能力。