如何在不关闭线程的情况下中断由某些套接字IO操作阻塞的线程

3

我很惊讶如何中断被某些I/O操作阻塞的线程?这个I/O是通过一个socket进行的,而我不想close()这个socket。

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

线程被I/O操作阻塞:

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

中断被阻塞的线程(未成功):

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

1
我认为只有在你可以访问底层流以关闭它时,你才无法停止它。 - user2511414
1
据我所知,它只有在阻塞操作可中断时才能正常工作。 - MadProgrammer
@MadProgrammer 是可中断的接口还是低级实现?! - user2889419
1
调用Socket.close()方法将关闭关联的InputStream和OutputStream对象,对于Socket本身的操作将抛出SocketException异常。 - Rahul Tripathi
@parsaporahmad 问题是,流的实现需要提供这个功能... - MadProgrammer
显示剩余2条评论
7个回答

3

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


3

谢谢,帮了很多忙,但这就像关闭套接字一样。 - user2889419

2
您可以添加一个类似于以下的方法,因为任何阻塞IO操作都会抛出IOException异常:-
public void close() throws IOException {
    this.socket.close();
}

根据javadoc的说明:

关闭此套接字。目前在accept()中被阻塞的任何线程都将抛出SocketException异常。


1
不!我不想关闭流,我想取消当前的读取操作并保持流开放。 - user2889419
1
但是,如果读取可能在消耗字节的中间,你如何保持套接字处于打开状态@parsaporahmad? - Gray
我并不是要关闭套接字,我想保持套接字的连接但暂时放弃读取。 - user2889419
1
@parsaporahmad - 听起来你想在套接字上设置读取超时,而不是写入超时。 - jtahlborn
@jtahlborn 大概是这样的。 - user2889419

1
请注意,没有声明抛出InterruptedException

read

public int read(byte[] b)
         throws IOException

Reads some number of bytes from the input stream and stores them into the buffer array b. The number of bytes actually read is returned as an integer. This method blocks until input data is available, end of file is detected, or an exception is thrown.

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

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


所以这将通过另一个线程来喂流,我不知道是否可能? - user2889419
有可中断的NIO通道。 - Gray

1
很遗憾,大多数平台上的Sun Java实现无法通过线程中断处理io中断(我认为你可以用一些nio东西来获得这个,不确定)。但是,我认为如果线程在从套接字中读取时被阻塞,并且你有该套接字的句柄,你可以从另一个线程异步关闭套接字(这将中断被阻塞的线程)。

请问您能否提供一些关于NIO的示例/链接?我正在尝试从网络/文件中读取数据,并希望能够中断读取线程。 - user2889419
@parsaporahmad - 大多数nio通道实现都扩展自http://docs.oracle.com/javase/6/docs/api/java/nio/channels/spi/AbstractInterruptibleChannel.html。 - jtahlborn

1
I/O操作和等待任何监视器的操作一样,都是不可中断的,这就是为什么这些情况下不需要InterruptedException例程的原因,但根据Java 7文档,属于nio包的I/O类可以响应任何尝试中断的操作,抛出其中一个流中断异常,其中之一是ClosedByInterruptionException以及其父类AsynchronousCloseException
但是,如果您想关闭执行线程而不是关闭流,则可以考虑使用FutureTask,它提供了一种取消的方式。

1
请看这篇博客文章,尤其是解决方案4 - 带缓冲的NIO
简而言之:对于常规文件,您必须使用NIO,使用扩展AbstractInterruptableChannelChannel,例如FileChannel
使用Socket#close()中断/关闭套接字时。
根据javadoc:

当前在此套接字上阻塞的任何线程将 抛出SocketException。

但是: 解决问题的另一种方法是使用带有NIO的异步IO,请参见此处。异步IO的问题在于,它非常复杂(与阻塞IO相比),因为您不再依赖流,而是依赖缓冲区。

谢谢,但我仍然需要关闭流,尽管我不想这样做。我不想关闭流,但我想取消当前的读取操作。 - user2889419
没有办法(使用阻塞操作)做到这一点。如果你真的必须要这样做,我建议你使用NIO异步方式。 - Ortwin Angermeier
1
嗯,我并没有看到NIO和IO之间有什么很大的区别,因为它们都会导致套接字关闭,只是在IO中线程关闭套接字,而在NIO中类会这样做。 - user2889419
当使用异步IO时,您不必关闭套接字。您可以随时跳出select()循环。 - Ortwin Angermeier

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接