【问题标题】:The Necessity of dispatching simple events with the EDT使用 EDT 调度简单事件的必要性
【发布时间】:2013-08-01 12:25:55
【问题描述】:

在继续讨论基本查询之前,我首先要声明,我完全意识到我的问题违反了 AWT/Swing 框架的标准。我的查询仅作为学术经验,不应该(希望)应用于现实世界的应用程序。

AWT/Swing 框架建立在基于事件的模型之上,该模型使用单个线程来分派事件。所有与 AWT/Swing 相关的事件事件都必须在事件调度线程 (EDT) 上进行处理,并且程序员编写的任何自定义事件都必须通过函数 invokeAndWait() 和 invokeLater() 进行排队。虽然这个模型确保框架永远不会遇到任何类型的线程并发问题,但它给试图围绕它编写代码的程序员带来了很大的痛苦(在 stackoverflow 上搜索 swing 问题......非常少)。

但是... 几年前,在我更熟悉 AWT/Swing 模型和 EDT 之前,我曾经编写过违反许多 Java 标准的代码(这些代码会让任何合理的程序员都感到恐惧)。我违反的这些标准之一是调用通过不是 EDT 的线程更新 GUI 的方法。准确地说,它是一个标准的“长”任务,它驻留在辅助线程上,该线程定期用当前进度更新 JLabel。现在回顾代码,我意识到尽管代码直接违反了标准,但它在 100% 的时间里都能正常工作。我注意到没有闪烁,没有文本损坏(因为它是 JLabel),没有抛出随机异常,也没有异常的 GUI 行为。当然,我从一个小例子中知道,不能简单地确定 AWT/Swing 标准是过度保护或不必要的。有了这个,我的问题在于:

对于像更新 JLabel 之类的简单任务(即使不是以恒定速率,可能每秒一次或两次)是否真的有必要通过 EDT 执行它?这可能带来的影响是什么(除了被整个 java 编程社区鄙视)(我想要一个可靠的影响列表,而不仅仅是“它可能会导致 EDT 搞砸”)?

假设一个模型,其中只有一个线程更新 GUI(不是 EDT)并且更新不频繁,并且仅在原子操作(更新字符串、原始数据等)中更新,程序是否有可能运行EDT 引起的问题(我猜这算作 hack?)。

作为一个挑战,我想知道是否有可能任何人都可以通过从另一个线程调度事件来证明违反 AWT/Swing 模型的代码,从而导致明显和持续(因为我不必等待 2 GUI 闪烁 1 帧的时间)问题?

顺便说一句,这可能是无关的,但是否会为新的 JFrame/Window 对象生成一个新的 EDT 线程,或者它们是否都运行在同一个线程上?我无法想象一个资源密集的多窗口系统都运行在一个线程上。

注意:我从来没有看过也没有分析过 AWT/Swing 框架的源代码,我所有的知识都是基于互联网研究和个人经验。以上如有错误,欢迎指正。 对于那些仍然对我上面的例子感到震惊的程序员,我已经更新了我的所有项目以符合标准(真是痛苦)。

【问题讨论】:

  • 请注意,在 JavaFX 中,如果您从另一个线程更新标签,则会出现异常。他们没有犯与 Swing 中相同的错误,即仅记录使用单个线程,而是真正强制执行。阅读this question 也可能是个好主意
  • 违反线程规则的代码似乎可以正常工作并不罕见。但是你做的地方越多,你在某个地方出现图形错误的可能性就越大。同一个程序在一个系统上看起来可以正常运行,但在另一个系统上或在不同的 jvm 版本上出现错误也是相当典型的。
  • @kiheru 我知道...但不同之处在于代码“实际上可以工作”...该特定程序已分发到大多数主要操作系统(由我...个人程序)并且他们都没有表现出奇怪的行为。 (在 WinXp/7/8、Mac OS X/Arch Linux/Ubuntu 和 Java 6 ~ Java 7 上测试)。我知道让协议失败仍然是不对的,我的问题是在适当的情况下需要协议。
  • @downvoter 请告诉我为什么这是一个糟糕的问题?
  • 这个问题相当没用,特别是如果你不断重复轶事“证据”;-) 是的,你可以打破规则,这有多酷......如果你如此热衷找到一个实际的破损,只需阅读一般线程问题并尝试驱动一些 ui 来展示这些问题。即使有可能(不知道),这也是浪费时间——因为重要的是 other 方式:gui 必须像任何其他应用程序一样正确、健壮、可维护。故意实施错误无助于实现这一目标。

标签: java swing awt event-dispatch-thread


【解决方案1】:

失败的示例:将您的JLabel 设置为右对齐。然后绘制需要两个步骤 - 测量文本,以便计算位置,然后绘制它。如果您在绘画时更改文本(例如,由于动画循环),文本似乎会偶尔跳跃。

回答您的另一个问题 - 所有 GUI 组件只有一个 EDT。

“持续而明显”的变化示例:假设 JLabel 具有 HTML 内容。在您的后台线程中,设置文本并触发重绘后,还会触发 PropertyChange,这会导致 UI 委托重新解析 HTML 并将其设置在客户端属性中(在后台线程中工作,尽管 UI假设它在 EDT 中,因为它正在接收事件)。

所以现在你有一个竞争条件:如果 UI 在后台线程中完成计算 HTML 视图之前重新绘制标签(在 EDT 中),它会绘制旧的 HTML,并且你的标签看起来不会更新。

你可以说,“那么,我不会在我的标签中使用 HTML。”但关键是这样的情况在 Swing 库中普遍存在——到处都有一个强烈的假设,即事件仅在 EDT 上传递,如果不阅读大量的 Swing 源代码,你不能保证你不会遇到这样的问题。

【讨论】:

  • 你能提供可以证明这一点的代码吗?而且,如果这就是 EDT 所保护的全部内容,那么上面给出的示例(更新不经常发生)应该是完全无风险的(或者故障的可能性非常小,不应该考虑)
  • +1 hmmm 对于所有 GUI 组件,只有一个 EDT。 != true :-) 在 Java7 中是否存在 SecondaryLoop,是的,我找不到 avoiding usage of eventQueue.push() 的一些实际用法,可以替代 avoiding usage of eventQueue.push()
  • @mKorbel:SecondaryLoop 仍然只有一个 EDT - 它的作用是从 enter() 中手动运行事件泵,直到调用 exit(),以便 @987654328 @ 似乎像显示模态对话框一样“阻止”。
  • @CPUTerminator 制作一个能够始终提供破坏行为的样本真的很困难,众所周知,竞争条件难以重现。虽然特定示例可能没有危险,但如果更新失败的标签是要删除并等待用户确认的表的名称怎么办?
  • @kleopatra 我知道这一点......我的问题不是“你什么时候不能使用 EDT”。鉴于上述情况,理论上是否可以绕过 EDT,如果不是,可能会出现什么问题。在上面的示例中,我没有看到任何竞争条件或并发修改。关键是理论上...我不打算找到可以绕过 EDT 的地方,而是更多地了解它的基本机制。如果您的回答是“YES YES YES”,此答案是否仅基于文档,或者您是否对它可能导致问题和/或可能导致什么问题有进一步的了解?
猜你喜欢
  • 1970-01-01
  • 2011-01-27
  • 1970-01-01
  • 2011-05-25
  • 2013-01-02
  • 1970-01-01
  • 2019-11-25
  • 1970-01-01
  • 2015-03-05
相关资源
最近更新 更多