【问题标题】:Trigger AutoClosable on JVM Shutdown在 JVM 关闭时触发 AutoClosable
【发布时间】:2018-04-26 10:51:43
【问题描述】:

TL;DR: 有没有办法找出,JVM-shutdown 只被我的代码启动的线程阻止?例如是否可以在关机时自动触发 AutoCloseable.close()?

上下文

我正在构建一个库,应该由多个客户使用。这意味着,除了提供文件之外,我不能强制执行某些事情。

架构

(我尽量把它描述得抽象一些,避免不必要的细节)

我有一个“管理器”对象(有点像工厂),用于创建一个“服务”对象,而该对象又需要一些数据来相应地工作。由于该数据是从一些“慢”后端服务加载的(也可能会不时更改),因此我使用一个单独的(守护进程)线程来检查更新并尽快将新数据注入该服务。 (这也意味着除非第一次更新,否则该服务只是处于“noop 模式”。但这没关系。)

现在“更新程序”(在我的守护线程中运行)使用了一个库,该库在打开连接时再次启动一个线程,并且有必要调用“关闭”以确保该辅助线程停止 - 否则不可能, 以正确关闭 JVM。

作为安全网,我在“Manager”的 finalize() 方法中调用 close() 方法(它保留对所有 Updater 实例的引用)。这不是 100% 安全的,因为它在 GC 运行时是不可预测的(在关机期间更是如此!),但这是我唯一的选择。

更新Here is some abstract example code that illustrates the architecture and the according problem

问题

这种架构会导致两个可能的缺陷:

  1. 如果实现不保留对管理器实例的引用,它将在某个时候被垃圾收集,并通过 finalize 方法停止必要的后台更新。

  2. 如果实现保留了管理器的实例,则必须在相应系统关闭期间调用close方法,否则JVM无法正常终止。

所以我的实际问题是使用该库的开发人员的“潜在不可靠性”。

有没有人知道如何构建一个可以处理这两个陷阱的解决方案? 最好有一些 Auto-AutoCloseable ;) 在关机期间调用(例如由 DestroyJavaVM 线程或类似线程)。

我尝试失败的解决方案

  1. 在更新程序中,我正在关闭“try-finally”块内的“有问题”连接,但该守护线程也不会自动中断/停止。

  2. 我注册了一个Runtime.getRuntime().addShutdownHook(...),它会关闭所有连接,但是这个关闭钩子永远不会被调用,因为只有在所有用户线程停止时才会启动关闭。

更新:在我的实施中解决了,但不是问题

我解决了我的问题,因为我发现第三方库(RabbitMQ 客户端)提供了一个 setThreadFactory 方法,我可以使用它来确保生成的线程是守护线程。 祝我的第 3 方库好运,但所描述的问题仍然可能发生。

【问题讨论】:

  • 你的第一句话是误导。据我了解,您说的是在不存在活动的非守护线程时发生的自动关闭。然后,您的问题是 not “强制停止用户线程 (daemon=false) on JVM 终止”,因为当用户线程没有发生 JVM 终止时还没有停止。所以第一个问题是,what 应该触发你的清理工作?只要线程处于活动状态,您的管理器就不会收集垃圾,但是当它们不活动时,您不需要进行此清理(那时,自动关闭将起作用)。
  • 你说得对,这正是我的困境。实际上,我想停止由在守护线程内运行的代码间接打开的用户线程。我会尝试用不同的方式表达。

标签: java multithreading architecture jvm software-design


【解决方案1】:

好的,我想这个问题没有很好的解决方案,但由于我发现我的第 3 方库提供了创建守护线程的可能性,这正是解决它所必需的。

也许这也是一个应该在“人类层面”解决的问题,方法是提供良好的文档并确保正确使用 AutoCloseable。一个好的开发者应该知道如何处理这个问题。

从技术角度来看,我发现了这些可能的解决方案,它们只是任何人都不应该依赖的“安全网”。

  • 在 finalize 方法中调用“close()”
  • 我实现了一项服务,可用于注册 AutoClosable 资源。它在守护线程内运行并每隔几秒检查一次,如果找到 JavaDestroyVM 线程,在这种情况下,关闭所有已注册的 AutoClosables 并自行停止。
    披露:如果系统在“主线程之外”运行方法”(JavaDestroyVM 线程将一直运行),这是解决方案不起作用。

更新:RegisterAutoClosable-Service 是一个非常丑陋/hacky 的解决方案 - 我将其删除并计划更改设计以避免此类情况,但最终实现开发人员有责任关闭正确打开资源。

【讨论】:

    【解决方案2】:

    我猜你希望 AutoCloseable 资源被关闭,这样关闭是有序的。

    AutoCloseable 对象应该(由您的库客户端)以确保在不再需要时关闭的方式使用。在几乎所有情况下,它们都应该使用 try-with-resources 块,因此即使抛出异常也会关闭它们。

    您应该通过要求您的库客户端在收到关闭程序的请求时对每个线程执行受控关闭来利用这一点。线程通过从每个 Runnable.run 方法返回或从每个 Runnable.run 方法抛出异常来执行受控关闭。我相信这是关闭资源的唯一可靠方法,因为它确保嵌套资源分配以正确的顺序重新分配。更一般地说,作为库编写者,您无法知道库客户端在关闭时可能想要执行的其他操作,因此您应该让他们完全控制关闭。

    您可以通过让您的库代码正确处理 InterruptedException 和 Thread.interrupted 标志来帮助他们做到这一点。

    【讨论】:

    • 我已经在这样做了。但是只要用户线程正在运行,我的守护线程就永远不会停止。请参阅随附的代码示例。我想你会建议我在每次运行时启动和停止连接,但这不是连接的最佳用法(我正在使用等待数据的消息队列)。
    猜你喜欢
    • 1970-01-01
    • 2023-03-24
    • 2012-10-26
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多