3

我有一个使用 addShutdownHook() 处理 Ctrl+C 的 Swing 应用程序,它可以正常工作,直到我有一个关闭任务调用一个函数,该函数在正常情况下会更改 JLabel 文本,此时它会挂起。

我认为问题在于 Swing EDT 要么已终止,要么正在等待某事。

有没有办法确定 EDT 已经终止或“完成”(这样我可以避免调用 Swing 方法),或者阻止 Ctrl-C 上通常的关闭所有窗口的行为?


只是为了澄清:

我在一个名为stop(). 在正常情况下,这可以被调用(连同它的补码start()),它会触发一系列导致 JLabel 更新的事情,以获得 astop()已经发生的视觉反馈。

当我的关闭挂钩运行时,我需要调用stop()以优雅地关闭一些资源。

我要问的是如何检测到 Swing EDT 不存在,所以我可以重写stop()以便它检测到缺少 Swing 并避免调用 Swing 函数。

4

3 回答 3

5

以下 hack 有助于确定 JVM 是否正在关闭过程中而不传递任何标志:

private static final Thread DUMMY_HOOK = new Thread();

public static boolean isShuttingDown()
{
    try {
        Runtime.getRuntime().addShutdownHook(DUMMY_HOOK);
        Runtime.getRuntime().removeShutdownHook(DUMMY_HOOK);
    } catch ( IllegalStateException e ) {
        return true;
    }

    return false;
}

但不要忘记可能的并发问题。

于 2011-11-10T15:14:46.217 回答
2

关闭挂钩不应期望其他服务处于已知状态(例如 UI 事件处理线程等),因为已请求关闭应用程序。编写一个关闭钩子来尝试更新不是 100% 在钩子控制范围内的东西可能会导致这种情况以及其他难以解决的行为。

addShutdownHook方法特别提到了这种情况,并警告您不应该尝试这些类型的操作,因为这样做很容易导致意想不到的后果,例如您观察到的锁定。

文档中

关闭挂钩在虚拟机生命周期的一个微妙时刻运行,因此应该进行防御性编码。特别是,它们应该写成线程安全的,并尽可能避免死锁。他们也不应该盲目依赖可能已经注册了自己的关闭钩子的服务,因此他们自己可能正在关闭的过程中。

在这种情况下,由于 Swing 是一个您无法控制的多线程 UI 系统,我建议不要在程序生命的关闭阶段尝试更改 UI 中的任何内容。这样做会导致奇怪的、不可预测的、有时是不可重复的情况,这些情况可能会从一次运行到另一次运行,或者从一台机器到另一台机器,具体取决于线程如何被安排关闭。另一个线程如何以及何时停止其工作并非旨在以线性、可预测的方式发生。因此,您在多线程程序中编写的任何代码都依赖于另一个线程在特定时间处于特定状态(除非这些线程清楚地相互交流它们的状态),都会让您了解这些类型的问题。

我想我的后续问题是,“为什么你想/需要在关机过程中更改 JLabel?” 如果您想在关闭之前更改某些内容的状态,更好的方法是将键盘输入作为常规事件捕获(防止它导致应用程序关闭),更改 JLabel 文本,然后开始关闭通过调用System.runFinalization()和/或System.ext(int)自己应用程序

于 2011-07-25T14:15:07.843 回答
0

我最终在关闭钩子的开头设置了一个标志,并用我的方法将这个(通过提前设置的对象)传达给对象stop(),以便它可以测试这个标志并决定是否调用 Swing 方法.

如果未设置关闭标志,则调用 Swing 方法。否则他们不会。

于 2011-07-25T16:37:23.950 回答