如何立即终止套接字 IO 操作上的线程阻塞?

Posted

技术标签:

【中文标题】如何立即终止套接字 IO 操作上的线程阻塞?【英文标题】:How to terminate a thread blocking on socket IO operation instantly? 【发布时间】:2011-05-24 10:41:33 【问题描述】:

在 Java 的上下文中,我创建一个新线程来在打开 GUI 窗口时读取网络输入,当我关闭窗口时,我想释放套接字资源并立即终止线程。现在我正在使用 setSoTimeout 方法,但我不想等待超时异常。有人可以提出一些建议吗?谢谢!

【问题讨论】:

【参考方案1】:

有(可能)三种方法可以做到这一点:

在套接字上调用Socket.close() 将关闭关联的InputStreamOutputStream 对象,并导致套接字中阻塞的任何线程或(关联的)流操作被解除阻塞。根据javadoc,对套接字本身的操作将抛出SocketException

调用Thread.interrupt() 将(在某些未指定的情况下)中断阻塞I/O 操作,导致它抛出InterruptedIOException

注意警告。显然,“interrupt()”方法不适用于“大多数”现代 Java 平台。 (如果其他人有时间和意愿,他们可能会调查这种方法的工作环境。但是,行为是特定于平台的这一事实应该足以说明您只应该在只需要您的应用程序时使用它在特定平台上工作。此时您可以轻松“尝试”自己。)

第三种可能的方法是调用Socket.shutdownInput() 和/或Socket.shutdownOutput()。 javadocs 没有明确说明当前被阻塞的读和/或写操作会发生什么,但认为它们会解除阻塞并抛出异常并不是不合理的。但是,如果 javadoc 没有说明会发生什么,则应假定该行为是特定于平台的。

【讨论】:

请问,在什么情况下thread.interrupt()会中断一个阻塞的I/O操作? Xu DXn - 没有记录。有些流实现了这一点,有些则没有。有关血腥细节,请阅读 OpenJDK 源代码。我想说的是,它可能有效,也可能无效。 很确定大多数 jvm 实现支持可中断的 io 操作。我想也许 sun solaris jvm 曾一度支持它。 作为补充,因为现在很多 Java 程序员实际上是在 android 上工作,需要注意的是,某些版本的 Android 在 Socket.close() 中有一个错误,阻止了这种技术在职的。但是,事先调用 Socket.shutdownInput() 确实有效。 @StephenC 有三种方法可以做到这一点。关闭输入将导致读取立即解除阻塞并返回 EOS 条件。 ClosedByInterruptException 仅适用于 NIO InterruptibleChannel,不适用于流或 Sockets【参考方案2】:

我知道这个问题很老,但似乎没有人解决“现代平台”上Thread.interrupt() 的“谜团”,所以我做了一些研究。

这是在 Windows7(64 位)上的 Java 8 上测试的(但其他平台也可能如此)。

调用Thread.interrupt()不会抛出InterruptedIOException 发生的情况是InputStream.read() 方法返回并设置了-1Thread.interrupted()-flag。

因此,以下内容可以被视为“已纠正”的 read() 抛出 InterruptedIOException

static final int read(Socket socket, byte[] inData) 
    throws SocketTimeoutException, // if setSoTimeout() was set and read timed out
           InterruptedIOException, // if thread interrupted
           IOException             // other erors

    InputStream in = socket.getInputStream();

    int readBytes = in.read( inData, 0, inData.length);

    if  ( Thread.interrupted() )
    
        throw new InterruptedIOException( "Thread interrupted during socket read");
    

    return readBytes;

【讨论】:

这个答案值得更多的赞成票,因为 Java 在输入/输出流操作期间被中断时的行为并不是很多人所期望的。 此方法仅在 InputStream 提供可读取的数据时有效。否则代码将被in.read(...) 阻塞,并且永远不会执行线程是否被中断的检查。

以上是关于如何立即终止套接字 IO 操作上的线程阻塞?的主要内容,如果未能解决你的问题,请参考以下文章

BIONIOAIO有什么区别?

套接字的多线程技术

高性能网络IO模型

pthread_kill()vs pthread_cancel()终止阻塞I / O的线程

JAVA I/O多路复用IO

BIO/NIO/AIO的区分