如何中断被某些套接字 IO 操作阻塞的线程而不关闭它

Posted

技术标签:

【中文标题】如何中断被某些套接字 IO 操作阻塞的线程而不关闭它【英文标题】:How to interupt a thread blocked by some socket IO operation without closing it 【发布时间】:2013-10-26 11:44:51 【问题描述】:

我真的很想知道如何中断被某些 I/O 操作阻塞的线程? I/O 是通过套接字,我不想close() 套接字。

我尝试使用Thread.interrupt() 中断线程,但没有成功。

线程被 IO 操作阻塞:

void run()
    byte[] data=new byte[1024];
    in.read(data);//blocked!

线程中断被阻塞的线程(没用):

void run()
    blockedThread.interrupt();//no success!

【问题讨论】:

我认为当您无法访问要关闭的底层流时,您甚至无法停止它。 AFAIK 只有在阻塞操作是可中断的情况下才会起作用 @MadProgrammer 是可中断的接口吗?还是低级实现?! @parsaporahmad 不,这是一个概念。需要发生的是,阻塞线程的任何操作都需要监视该线程的中断状态,以使其可中断 在套接字上调用 Socket.close() 将关闭关联的 InputStream 和 OutputStream 对象,并且对套接字本身的操作将抛出 SocketException。 【参考方案1】:

在获取SocketTimeoutException 时,在套接字上设置一个较短的读取超时并循环,每次都检查Thread.isInterrupted()。使用Thread.interrupt()中断线程。

【讨论】:

【参考方案2】:

I/O 之类的等待任何监视器的操作是不可中断的,这就是这些情况不需要 InterruptedException rutine 的原因,但根据 Java 7 Documentation 属于 @ 的 I/O 类987654326@ 包可以响应任何被中断的尝试,抛出流中断异常之一,其中之一是ClosedByInterruptionException 及其父类AsynchronousCloseException

但如果您想关闭执行头,而不是关闭流,您可以考虑使用提供取消方式的FutureTask

【讨论】:

【参考方案3】:

看看this blog post,尤其是解决方案 4 – 带缓冲的 NIO

简而言之:对于常规文件,您必须使用NIO,使用扩展AbstractInterruptableChannelChannel,例如。 FileChannel.

当使用 sockets 时,使用Socket#close() 来中断/关闭。

根据 javadocs:

当前在此套接字上的 I/O 操作中阻塞的任何线程都将 抛出一个 SocketException。

但是:

解决这个问题还有另一种方法:使用NIO 的异步IO,看看here。 异步 IO 的问题在于,处理起来非常复杂(与阻塞 IO 相比),因为您将不再中继流,而是使用缓冲区。

【讨论】:

谢谢,但我仍然需要关闭流,而我不想这样做。我不想关闭流,但我想取消当前的读取操作。 没有办法做到这一点(使用阻塞操作)。如果你真的需要,我建议你异步使用NIO 好吧,我在这里看不出 NIO 和 IO 之间有什么大的区别,因为两者都会导致关闭套接字,除了在 IO 线程中关闭套接字,而在 NIO 中,类这样做. 使用异步 IO 时,您不必关闭套接字。你可以随时跳出select()的循环。【参考方案4】:

如果您绝对不能/不想使用普通的 java Socket 类关闭套接字,您可以使用 java.nio.SocketChannel 来处理套接字。

SocketChannel 实现了InterruptibleChannel,可以通过中断在套接字上等待 I/O 的线程来关闭它。在这种情况下,被阻塞的读取会抛出一个ClosedByInterruptException

【讨论】:

谢谢,帮了大忙,不过就像关闭套接字一样。【参考方案5】:

您可以添加这样的方法,因为任何阻塞 IO 操作都会抛出 IOException:-

public void close() throws IOException 
    this.socket.close();

来自 javadoc:-

关闭此套接字。当前在 accept() 中阻塞的任何线程都将 抛出一个 SocketException。

【讨论】:

不!我不想关闭流,我想取消当前的读取操作并保持流打开。 但是,如果读取可能处于消耗字节的中间@parsaporahmad,如何保持套接字打开? 我不是说关闭套接字,我想让套接字保持活动状态但暂时放弃阅读。 @parsaporahmad - 听起来您想在套接字上设置读取超时。 @jtahlborn 类似的东西【参考方案6】:

注意没有InterruptedException 声明被抛出:

阅读

public int read(byte[] b)
         throws IOException

从输入流中读取一些字节并将它们存储到缓冲区数组 b 中。实际读取的字节数以整数形式返回。在输入数据可用、检测到文件结尾或引发异常之前,此方法会一直阻塞。

一些读取方法会通过Thread#interrupt()响应外部中断,要么关闭流,要么抛出异常。

您不能否则停止该线程,除非以某种方式关闭或从另一个线程提供流,或者使用Thread#stop() 终止线程。最后一种方法只是最后的手段,不应出于各种原因使用。

【讨论】:

所以通过另一个线程提供流会很好,我不知道是否可能? 有可中断的 NIO 通道。【参考方案7】:

不幸的是,大多数平台上的 sun java impl 不通过线程中断处理 io 中断(我相信你可以通过一些 nio 的东西来解决这个问题,不确定)。但是,我相信如果线程被阻塞从套接字读取,并且您有该套接字的句柄,您可以从另一个线程异步关闭套接字(这将中断被阻塞的线程)。

【讨论】:

你能举一些关于 nio 的例子/链接吗?我正在尝试从网络/文件中读取数据,我会中断读取线程。 @parsaporahmad - 大多数 nio 通道实现都扩展了 docs.oracle.com/javase/6/docs/api/java/nio/channels/spi/… 。

以上是关于如何中断被某些套接字 IO 操作阻塞的线程而不关闭它的主要内容,如果未能解决你的问题,请参考以下文章

java 多次new DataOutputStream而不关闭,线程阻塞

如何使用 executor.execute 和 future.get() 结束任务(使线程超时)(通过上升中断)而不阻塞主线程

Boost::asio - 如何中断阻塞的 tcp 服务器线程?

如何关闭在while循环中的线程中侦听的阻塞套接字?

如何中止winsock阻塞调用?

如何停止一个正在运行的线程?