【问题标题】:ClosedByInterruptException not thrown未抛出 ClosedByInterruptException
【发布时间】:2011-04-02 06:26:44
【问题描述】:

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() 调用也会正常返回。不会抛出异常。此代码打印“线程中断”和“通道关闭”,然后在下一次调用 read() 时抛出 ClosedChannelException。

我想知道这种行为是否被允许。据我了解文档,read() 应该正常返回而不关闭通道或关闭通道并抛出 ClosedByInterruptException。正常返回并关闭频道似乎不对。我的应用程序的问题是,当执行 io 的 FutureTask 被取消时,我在其他地方得到了一个意外且看似无关的 ClosedChannelException。

注意:当线程在进入 read() 时已经被中断时,将按预期抛出 ClosedByInterruptException。

我在使用 64 位服务器虚拟机(JDK 1.6.0 u21,Windows 7)时看到了这种行为。谁能证实这一点?

【问题讨论】:

    标签: java multithreading exception io


    【解决方案1】:

    我记得在某处读过,但我无法在此处引用消息来源,FileChannel 有点可中断。一旦读/写操作从 JVM 传递到 OS,JVM 就无法真正做很多事情,因此操作将花费它所花费的时间。建议以可管理大小的块进行读/写,以便 JVM 可以在将作业处理给操作系统之前检查线程中断状态。

    我认为你的例子完美地展示了这种行为。

    编辑

    我认为您描述的FileChannel 行为违反了“最小意外”原则,但从某个角度来看,即使您根据需要订阅该角度,也可以正常工作。 p>

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

    我认为这种行为应该真的真的有据可查,因此您可以提交一个错误。但是,不要屏住呼吸,因为它已经被修复了。

    我认为判断线程是否在同一个循环迭代中被中断的唯一方法是做你正在做的事情并明确检查线程中断标志。

    【讨论】:

    • 我想我在“Java NIO”一书中读到过这个,但我现在手边没有。
    • 这与时间无关。显然,VM 检测到线程中断:它关闭了通道。但这里的问题是,它确实 not 像宣传的那样抛出 ClosedByInterruptException。请注意,我的代码中的任何地方都没有 close() 调用,中断的 read() 也没有抛出异常。虚拟机只是默默地关闭通道而不通知。
    • @Gerald。我在答案中添加了更多内容。
    • 感谢您对亚历山大的意见。我同意 read() 如果它已经有数据正常返回是有意义的。对于那件事我没有任何疑问。但是为什么他们还要默默地关闭频道呢?当他们正常返回时,为什么不让它不管呢?这对我来说没有意义。无论如何,我必须重新检查频道是否关闭并自己关闭它,因为我不知道中断发生在哪里。这一切看起来真的很奇怪,每当我调用 read() 时,它都会迫使我编写复杂的代码。我会提交一个错误报告。这种行为至少应该记录在案。
    • @Gerald。我认为他们关闭频道是半心半意地尝试遵循 InterruptibleChannel 的设计规范(频道被中断,然后频道被关闭)。这绝对可以防止对该通道的进一步操作。我认为这里最好的解决方案是抛出 ClosedByInterruptException 的扩展并将已读取的数据包装在该新异常类中。
    【解决方案2】:

    一种可能性是您的程序实际上在中断传递之前到达了文件末尾。当你看到当前线程被中断时,试着改变你的程序来计算读取和输出的字节总数。

    【讨论】:

    • 不是这样,read()的返回值为正,我的testfile很大。另请注意,在文件末尾 read() 只会返回 -1 但不会关闭通道。
    猜你喜欢
    • 1970-01-01
    • 2011-11-17
    • 1970-01-01
    • 2019-02-15
    • 2020-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多