JDK文档说,如果线程被中断,当前在InterruptibleChannel的io操作中被阻塞,则关闭该通道并抛出ClosedByInterruptException。但是,使用FileChannel时会出现不同的行为:
public class Main implements Runnable {
public static void main(String[] args) throws Exception {
Thread thread = new Thread(new Main());
thread.start();
Thread.sleep(500);
thread.interrupt();
thread.join();
}
public void run() {
try {
readFile();
} catch (IOException ex) {
ex.printStackTrace();
}
}
private void readFile() throws IOException {
FileInputStream in = new FileInputStream("large_file");
FileChannel channel = in.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(0x10000);
for (;;) {
buffer.clear();
// Thread.currentThread().interrupt();
int r = channel.read(buffer);
if (Thread.currentThread().isInterrupted()) {
System.out.println("thread interrupted");
if (!channel.isOpen())
System.out.println("channel closed");
}
if (r < 0)
break;
}
}
}
这里,当线程被中断时,read()调用正常返回,即使通道已经关闭。没有异常被抛出。此代码打印“thread interrupted”和“channel closed”,然后在下一次调用read()时抛出ClosedChannelException。
我不知道这种行为是否允许。据我所知,read()应该正常返回而不是关闭通道或关闭通道并抛出ClosedByInterruptException。正常返回并关闭频道似乎不对。我的应用程序的问题是,当一个执行io的FutureTask被取消时,我在其他地方得到一个意外且看似无关的ClosedChannelException。
注意:当进入read()时线程已被中断时,将按预期抛出ClosedByInterruptException。
我使用64位服务器-VM(JDK 1.6.0 u21,Windows 7)看到了这种行为。谁能证实这一点?
答案 0 :(得分:2)
我记得在某处读过,但我不能在这里引用来源,FileChannel
有点可以打断。一旦读/写操作从JVM传递到OS,JVM就不能真正做很多事情,因此操作将花费时间。建议使用可管理的大小块进行读/写,因此JVM可以在将作业处理到OS之前检查线程中断状态。
我认为你的例子是对这种行为的完美证明。
修改强>
我认为您描述的FileChannel
行为违反了“最少意外”的原则,但是从某个角度按预期工作,即使您根据需要订阅了角度。
因为FileChannel
是“有点”可中断的,并且读/写操作确实是阻塞的,所以阻塞操作确实成功并返回表示磁盘上文件状态的有效数据。如果是小文件,您甚至可以获取文件的全部内容。因为你有有效的数据,'FileChannel`类的设计者觉得你可能想要在开始解除中断之前使用它。
我认为这种行为应该真的,真的有充分的文档记录,因此您可能会提交错误。但是请不要屏住呼吸。
我认为,如果线程在同一循环迭代中被中断,唯一的方法就是做你正在做的事情并明确检查线程中断标志。
答案 1 :(得分:0)
一种可能性是程序在中断传递之前实际到达文件末尾。尝试更改程序以计算当您看到当前线程被中断时读取和输出的总字节数。