java – 不抛出ClosedByInterruptException

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)的这种行为.谁能证实这一点?

最佳答案 我记得在某处读过,但我不能在这里引用来源,FileChannel有点可以中断.一旦读/写操作从JVM传递到OS,JVM就不能真正做很多事情,因此操作将花费时间.建议是以可管理的大小块读/写,因此JVM可以在处理作业到OS之前检查线程中断状态.

我认为你的例子是对这种行为的完美证明.

编辑

我认为你描述的FileChannel行为违反了“最少惊喜”的原则,但是从某个角度按预期工作,即使你按照预期订阅了那个角度.

因为FileChannel是“有点”可中断的,并且读/写操作确实是阻塞的,所以阻塞操作确实成功并返回表示磁盘上文件状态的有效数据.如果是小文件,您甚至可以获取文件的全部内容.因为你有有效的数据,’FileChannel`类的设计者觉得你可能想要在开始解除中断之前使用它.

我认为这种行为应该是真的,非常好的文档,为此您可能会提交错误.然而,不要屏住呼吸固定.

我认为,如果线程在同一循环迭代中被中断,唯一的方法是执行您正在执行的操作并明确检查线程中断标志.

点赞