【问题标题】:Halting the Event Dispatch Thread activity temporarily (v7)暂时停止 Event Dispatch Thread 活动 (v7)
【发布时间】:2024-04-29 15:00:02
【问题描述】:

好的,所以解释这种情况有点复杂。它实际上并不像看起来那么危险。

我正在编写一个并发包,它做两件事,它曾经在 JDK 6 上完美运行。它所做的第一件事是提供一个更好的Future 接口,它允许在任务完成时使用事件侦听器进行回调。 “但你可以使用FutureTaskdone()!”你可能会说。问题是当一个任务被取消时,done() 会立即被调用,而该任务可能仍在其call() 方法中做一些事情。此外,取消使得无法检索部分结果。所以我自己的包通过扩展现有的包来解决这些问题(包括一个新的线程池,但大部分实现都在那里重用)。

包做的第二件事是解决一个常见的 GUI 问题。假设用户单击“保存”以保存图像。如果图像很大,最好显示进度监视器而不是冻结界面。但是如果文档很小,那么进度监视器的突然闪烁会使用户感到困惑。而且我们不能有最小的弹出时间 - 这会减慢工作流程。我的解决方案在这两者之间妥协。效果是 EDT “冻结”了任务完成所需的时间,或者,如果花费的时间超过 1 秒,则显示一个具有最短弹出时间的进度监视器。效果相当优雅。这在 JDK6 中也可以完美运行,甚至可以使用多个线程。

问题:在 JDK7 中,上面的冻结部分现在有一个微妙的错误。我的代码的一个关键假设是传递给SwingUtilities.invokeLater()Runnable不会 与其他事件调度线程的东西同时运行。所以 EDT 不能在 Runnable 运行时处理按钮点击、加速器、其他事件等。 (这被用来在冻结期间停止所有输入)但这似乎在 JDK7 中发生了变化。在我的调试中,我注意到Runnable 可能正在运行,而按下按钮的代码仍在运行!由于后来的事情,这很快就会导致死锁。由于实施错误,没关系。

像这样滥用 EDT 是有风险的,但在过去,这是唯一有效的方法。一些人建议使用GlassPane 来拦截事件。但它不能拦截加速器。我当时尝试的其他一切也都以这种方式受到限制。

我现在的问题是,是否有人找到更好的方法来做到这一点。如果您认为我的Future 问题可以以更好的方式解决,请告诉我。最重要的是,如果您能找到解决上述 GUI 问题的方法,或者停止输入,或者保证我在 JDK 上需要的关键假设,或者提出长期解决方案 - 所有这些都非常感谢。谢谢!

【问题讨论】:

  • SwingUtilities.invokeXxx 在事件调度线程的上下文中执行Runnable,所以我看不出如何,当执行Runnable 时,您可能正在执行其他代码EDT 的上下文,因为Runnable 将阻塞 EDT,直到它完成。这不会阻止事件被堆叠,只是被处理。您可以考虑使用JXLayer 或(Java 7 中的JLayer)来禁用/阻止部分 UI...
  • 好的,将研究 JLayer。同时,让我制作一个 sn-p 来向您展示这种效果。
  • ACH!这与 Java 版本无关。这是由于一个微妙的实现错误。无论如何,我可能会将 JLayer 视为一种更强大的方式来做我想做的事。对于问题的第一部分,我将保留问题。谢谢!
  • I don't believe,为了获得更好的帮助,请尽快发布 SSCCE,简短,可运行,可编译,由上午问题引起,or there,...等
  • 您知道,当我们第一次使用我们的应用程序测试 Java 7 时遇到了死锁问题,该问题发生在 Swing.invokeLater。但是在我为该部分重新编写代码后,它现在可以工作了。

标签: java multithreading swing user-interface


【解决方案1】:

问题的第一部分是否与更好地使用 Future 有关?

... done() 会在任务被取消时立即调用,而该任务可能仍在其 call() 方法中执行操作。

您是否在call() 方法中手动检查了Thread.interrupted()

我们知道FutureTask.cancel()的源代码,它只是调用Thread.interrupt(),这意味着只有一个代表线程中断状态的布尔标志才会被设置为true。除非调用Thread.sleep().wait().join(),否则不会抛出InterruptedException,因为后3 个它们确实会检查标志并做出响应。

A sample is here,请阅读已接受的答案。

此外,取消也无法检索部分结果。

是的。您可以将部分结果泄露给任务外的Hashmap。或者直截了当,将结果初始化为外部的最终变量,然后在内部使用它(call() & done())。其实我以前曾经将百分比字段保存到 KV 存储中。

【讨论】: