Java线程和关机钩子

时间:2013-07-18 15:48:00

标签: java multithreading sigint shutdown-hook

我刚遇到一个有趣的问题。看来,如果在Java中,一个线程调用System.exit(),那么它就不能通过Thread.join()加入。

这导致了我的问题,因为我想在我的应用程序之后使用关闭钩子来清理,例如:

Runtime.getRuntime.addShutdownHook(new Thread() {
    public void run() {
        reader.join();
        writer.join();
        in.close();
        out.close();
    }
});

这个想法是,在关闭这些资源之前,它确保线程已完成各自的资源。问题是有3种情况可以调用关闭钩子。他们是:

  1. 用户点击[ctrl] + [C]。
  2. 阅读器线程完成,并调用System.exit()
  3. Writer线程完成,并调用System.exit()
  4. 用户点击[ctrl] + [C]的第一种情况正常。但在其他两种情况中,关闭挂钩永远阻塞。这是一个事实的连锁效应,即一个Thread.join(),一个已经调用System.exit()的线程被调用,永远阻止。

    因此,我有两个问题。首先,我知道我可以使用Thread.join(long millis)代替它们,以便它们不会无限期地阻止,但任何人都可以想到更优雅的解决方案吗?其次,虽然可以两次针对同一个线程调用Thread.join(),但是第二次它会立即返回,有没有人知道为什么调用Thread.join()针对已调用{{1}的线程的原因无限制地阻止而不仅仅立即返回?

4 个答案:

答案 0 :(得分:2)

System.exit如果成功,即使通过抛出异常也不返回,所以线程永远不会完成。这在关闭钩子之前不是问题。

解决方法是使用通常的标准锁(甚至只使用new Thread(new Runnable() { public void run() { System.exit(0); }}).start();进行破解)。

答案 1 :(得分:1)

您正在做的是specifically prohibited

  

关机挂钩也应该快速完成工作。

加入随机线程不能被描述为“快速”。

显然,这些线程在退出时应关闭其自己的流。如果他们不退出,调用join()将如何完成任何事情?

答案 2 :(得分:0)

不建议使用System.exit(),因为它只是终止JVM。还有其他并发工具,如CountDownLatch。您甚至可以在关机时使用finally来正确清理。

答案 3 :(得分:0)

使用执行程序,不要使用join或手动创建线程。

当使用执行程序和java.util.concurrent中的其他内容时,您可以简单地调用shutdown并等待它终止,指定超时等等。更加健壮。如果您使用的是java 1.7,请使用try with resources来关闭您的流。这样,除了关闭执行程序之外,您没有太多的清理工作。如果没有,请使用finally块。永远不要在最后一个街区以外的任何地方打电话。