优雅地退出应用程序?

8
我有一个应用程序,其中包含一个明确定义的Try/Catch/Finally链,在正常情况下可以成功退出并执行finally块。然而,当某人在GUI中提前点击红色X时,程序完全退出(代码=0),主线程的finally块不会被调用。
实际上,我希望程序在单击红色X时退出,但我不希望跳过finally{}块!我在GUI中手动放置了最重要的finally块部分,但我真的不想这样做,因为我希望GUI与实际程序解耦。
class GUI { // ...
...
mainFrame.addWindowListener(new WindowAdapter() {
  public void windowClosing(WindowEvent evt) {
    try {
      processObject.getIndicatorFileStream().close();
    } catch (Exception ignore) {}
    System.exit(0);
  }
});
...
}

但我更希望像这样进行通话:

mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

请确保在每个线程退出后,所有的finally{}块都被调用。

我知道这实际上是期望的。如果应用程序从另一个线程(比如GUI线程)关闭,那么主线程就会停在它的轨迹上。

简而言之,我该如何确保System.exit(0)或JFrame.EXIT_ON_CLOSE仍会导致每个线程的finally块执行?

3个回答

8
如果您没有其他设计更改的选择,那么您可能需要的是一个JVM关闭挂钩(JVM shutdown hook),当调用System.exit时可以添加它来运行一段代码。
“Shutdown Hooks”是一种特殊构造,允许开发人员插入一段代码,在JVM关闭时执行。这在需要进行特殊清理操作以防VM关闭的情况下非常方便。
您可以按照此处所述添加关闭挂钩:
Runtime.getRuntime().addShutdownHook(Thread)

点击此处了解有关关闭挂钩的更多信息:

http://java.dzone.com/articles/know-jvm-series-2-shutdown

注意事项:

需要牢记的是,不能保证关闭挂钩总是会运行。如果JVM由于某些内部错误而崩溃,则可能在没有执行单个指令的机会下崩溃。另外,如果操作系统发出SIGKILL (http://en.wikipedia.org/wiki/SIGKILL)信号(在Unix / Linux中为kill -9,在Windows中为TerminateProcess),则应用程序需要立即终止,而不进行任何清理活动。除上述之外,还可以通过调用Runime.halt()方法终止JVM,而不允许关闭钩子运行。


1
值得注意的是,这并不保证始终运行(例如当JVM崩溃时)。 - DannyMo
1
@damo 是的,这是一个重要的点。让我把它加到答案中。 - Juned Ahsan
1
这是错误的建议。Shutdown hooks 永远不应该作为常规退出程序的一部分使用。它们只是最后的备选方案,在所有其他方法都失败时,才能挽救可以挽救的东西。 - Marko Topolnik

3
如果你恰好有这样的线程,它们可以在循环中的任何点,在调用的任何方法中的任何点上合法地停止,并且我要警告你,这种情况非常不可能发生,那么你可以在程序退出时停止它们全部。这将导致每个线程抛出异常,并且finally块将执行。
然而,实现你的目标并使GUI与程序逻辑分离的正确方法是从GUI发出单个“退出”信号,这将触发编写在完全不同类中的所有应用程序清理。如果有运行中的线程,则在每个线程中实现interrupt机制。
有许多方法可以实现退出信号。例如,你的业务代码可以为特殊事件注册GUI监听器,该事件将触发清理。你也可以有一个线程除了await一个从GUI中countDownCountDownLatch以外什么都不做。
千万不要使用关闭挂钩。这是最肮脏的机制,只有在所有常规清理过程失败时才会使用。它永远不应作为常规关闭例程的一部分。
总之,没有通用的方式来清理应用程序关闭。你必须为每个具体问题实现特定的机制。

有任何示例代码吗?我明白你的意思,听起来很合理(我基本上正在寻找这个东西的示例)。 - E.S.
你能详细说明一下吗?或者给一些链接或短语供谷歌搜索吗? - Andrzej Rehmann
1
@hoto 可以搜索 Thread#interrupt;另一个是 CountDownLatch;第三个是 AWT 自定义事件 - Marko Topolnik

1

使用现代Java,对所有应用程序窗口执行Window.dispose()可以比System.exit(0)提供更优雅的退出AWT应用程序的可能性,参见
https://docs.oracle.com/javase/8/docs/api/java/awt/Window.html#dispose--

/** Listens and closes AWT windows.
 * The class is implemented as singleton since only one is needed.
 */
public class ExitListener extends WindowAdapter {

  /** the instance object */
  private static final ExitListener INSTANCE = new ExitListener();

  // hide the constructor
  private ExitListener () {}

  /** retrieve the listener object */
  public static ExitListener getInstance () {
    return INSTANCE;
  }

  @Override
  public void windowClosing ( final WindowEvent e ) {
    e.getWindow().dispose();
  }
}

还有您的Windows操作系统

window.addWindowListener( ExitListener.getInstance() );

然而,在恶劣的环境下要小心,参见:
https://docs.oracle.com/javase/8/docs/api/java/awt/doc-files/AWTThreadIssues.html#Autoshutdown


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接