在 JVM 关闭时触发 AutoClosable

Trigger AutoClosable on JVM Shutdown

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

上下文

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

建筑

(我尽量描述的比较抽象,避免不必要的细节)

我有一个 "Manager" 对象(有点像工厂),它用于创建一个 "Service" 对象,而该对象又需要一些数据才能相应地工作。由于该数据是从某些 "slow" 后端服务加载的(也可能会不时更改),我使用一个单独的(守护进程)线程来检查更新并尽快将新数据注入该服务。 (这也意味着除非第一次更新,否则该服务只是在 "noop mode"。不过没关系。)

现在 "Updater"(在我的守护线程中运行)使用一个库,它在打开连接时再次启动一个线程,并且有必要调用 "close" 以确保该辅助线程停止- 否则无法正确关闭 JVM。

作为安全网,我在 "Manager" 的 finalize() 方法中调用了 close() 方法(它保持对所有更新程序实例的引用)。这不是 100% 安全的,因为它在 GC 运行时不可预测(甚至在关闭期间更是如此!),但这是我唯一的选择。

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

问题

此体系结构导致两个可能的陷阱:

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

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

所以我的实际问题是 "potential unreliability" 正在使用该库的开发人员。

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

我尝试失败的解决方案

  1. 在更新程序内部,我正在关闭 "try-finally" 块内的 "problematic" 连接,但守护线程也不会自动中断/停止。

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

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

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

我想您希望关闭 AutoCloseable 资源以便按顺序关闭。

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

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

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

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

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

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

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

更新:RegisterAutoClosable-Service 是一个非常ugly/hacky 的解决方案 - 我删除了它并计划更改设计,以避免这种情况,但最终它是实施开发人员有责任正确关闭打开的资源。