将 Java 套接字从线程 A 传递到 B

Passing a Java socket from thread A to B

在服务器中,有一个线程 A 监听传入的连接,通常会永远循环。当连接被接受时,线程 A 创建一个任务(例如,class Callable in Java)并将其提交给 Executor。 这一切实际上意味着 A 丢失了对套接字的引用,现在有一个线程 B(由执行器创建)管理套接字。如果B遇到任何异常,它会关闭套接字,并且不存在套接字作为操作系统资源不会被回收的风险。

如果线程 B 启动,这一切都很好。但是,如果在 B 有机会被调度之前执行程序被关闭怎么办?

有人认为这是个问题吗?如果套接字的引用因此丢失,垃圾收集器会关闭套接字吗?

是的,这听起来像是个问题。

OS 可能最终会释放套接字(至少如果是 TCP,据我所知),但可能需要相对较长的时间。

我不认为垃圾收集器在这种情况下发挥作用。至少对于线程来说不是,即使在代码中没有引用它们,线程在启动后通常会保持 运行 (这至少对于非守护线程是正确的)。套接字可能以类似的方式运行。

如果您不能保证连接将被处理(通过在建立后立即启动处理 Thread 实例),那么您应该保留对套接字的引用并确保关闭所有他们尽快,这可能意味着在 Executor.shutdown() 或类似方法被调用之后。

请注意,根据您要求执行器关闭的方式,它会处理或不处理已经提交执行但尚未启动的线程。所以一定要让你的代码有相应的行为。

此外,如果您处理传入套接字连接的资源(可用线程)有限并且不希望它们增长太多,请考虑在接受后立即关闭它们,这样它们就不会堆积在未处理的等待中队列,如果这在您的项目中可行。然后客户端可以稍后重试连接。如果您仍然需要在连接进入后立即使用它们,请考虑一种非阻塞 I/O 方法,这种方法往往会更好地扩展(并且达到一定程度)。

If the reference to the socket is lost due to this, would the garbage collector close the socket?

可能吧。但是垃圾收集器可能 运行 直到下周结束:你不能依赖 GC 运行ning,几乎永远,仅仅因为 'hey, java has a garbage collector'。确实如此,直到需要时才会启动。它可能根本不需要。

依靠 GC 来关闭资源是让您的 VM 因 OS 使用过多系统资源而被杀死的好方法。

真正的问题是:导致执行程序关闭的因果过程是什么?

如果有某种 'cancel all open connections' 按钮,并且您将其实现为一行: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 你有一个更大的问题 - 如果在处理它的过程中发生异常,套接字将不会被清理!)。

一个出路是有一个套接字列表,抽象掉队列本身,并让抽象有一个关闭方法,两者 关闭队列and 关闭每个套接字,用 try/catch 块保护每个步骤(队列关闭以及所有 socket.close 命令)以确保这些步骤之一中的单个异常不会就地停止关机过程。

请注意,一堆处理程序可能仍在运行,因此像这样关闭套接字 'out from under them' 将导致处理程序出现异常。如果你不想那样,关闭队列,然后等待终止(用try/catch东西保护),然后然后关闭所有套接字。

您可以关闭一个已关闭的套接字,即 noop,无需先检查,也无需担心关闭大量已关闭套接字的影响。

但是请担心将 obj 引用保存到无限增长的套接字列表中。一旦一个套接字完全用完了,就把它去掉——同样来自这个 'stuff you need to close if the queue is terminated'.

的精选列表

当然,如果导致队列提前终止的唯一进程是因为您想关闭 VM,请不要担心。套接字随 VM 一起消失。事实上,不需要关闭队列。如果你打算结束虚拟机,就……结束它。立即: System.shutdown(0) 就是你想要的。没有'but.. I should ask all the things to shut down nicely!'这样的东西。那就是你问的方式。需要清理资源的系统大多设计不当(将它们设计为在 VM 关闭时不需要清理。例如,所有资源都以这种方式工作),如果必须,请注册一个关闭挂钩。