如果对套接字的引用因此丢失,垃圾收集器会关闭套接字吗?
大概吧。但是垃圾收集器可能要到下周末才能运行:你不能依赖 GC 运行,几乎永远,只是因为“嘿,java 有一个垃圾收集器”。确实如此,而且在需要之前它不会启动。它可能根本不需要。
依靠 GC 来关闭资源是让你的 VM 因使用过多系统资源而被操作系统杀死的好方法。
真正的问题是:导致执行者关闭的因果过程是什么?
如果有某种“取消所有打开的连接”按钮,并且您将其实现为单行:queue.shutdown(),那么,不 - 这不是一个好主意:您现在将依靠GC 来清理那些坏的套接字。
我假设您的可调用对象看起来像:
Socket socket = ....; // obtained from queue
Callable<Void> socketHandler = () -> {
try {
// all actual handling code is here.
} finally {
socket.close();
}
return null;
};
那么是的,这是一个问题:如果可调用对象甚至从未启动,则 finally 块将不会运行。 (如果你没有finally,你有一个更大的问题——如果在处理它的过程中发生异常,那个套接字将不会被清理!)。
一种出路是拥有一个套接字列表,抽象出队列本身,并让该抽象具有一个关闭方法,两者都会关闭队列并且关闭每个套接字,用 try/catch 块保护每个步骤(队列关闭以及所有 socket.close 命令),以确保这些步骤之一中的单个异常不会立即停止关闭过程。
请注意,一堆处理程序可能仍在运行,因此像这样关闭套接字“从它们下面”会导致处理程序出现异常。如果您不希望这样,请关闭队列,然后等待终止(使用 try/catch 进行保护),然后然后关闭所有套接字。
你可以关闭一个关闭的socket,也就是noop,不需要先检查,也不需要担心关闭大量已经关闭的socket的影响。
但要担心将 obj ref 保留到无限增长的套接字列表。一旦一个套接字完全完成,就摆脱它 - 也可以从这个精选的“如果队列终止,你需要关闭的东西”列表中。
当然,如果导致队列提前终止的唯一进程是因为您要关闭 VM,请不要担心。套接字与 VM 一起消失。事实上,没有必要关闭队列。如果您打算结束 VM,只需.. 结束它。立即:System.shutdown(0) 是你想要的。不存在“但是..我应该要求所有的东西都很好地关闭!”这样的东西。这就是你问的方式。需要清理资源的系统大多设计得很糟糕(将它们设计为不需要在 VM 关闭时进行清理。例如,所有资源都以这种方式工作),如果必须,请注册一个关闭挂钩。