为什么关闭一个输入流会同时关闭关联的文件描述符,即使该文件描述符被多个流共享?

3

我刚刚在查看FileInputStream的实现时,对其close方法的实现感到困惑,如下所示:

public void close() throws IOException {
        synchronized (closeLock) {
            if (closed) {
                return;
            }
            closed = true;
        }
        if (channel != null) {
           channel.close();
        }

        fd.closeAll(new Closeable() {
            public void More ...close() throws IOException {
               close0();
           }
        });
}

这将在FileDescriptor上内部调用closeAll,它会释放所有可关闭的资源(不仅是调用 close 的特定实例)。因此,对输入流的关闭会关闭该文件描述符共享的所有流。

    FileInputStream is = new FileInputStream("Some file");
    BufferedInputStream br = new BufferedInputStream(is);

    FileDescriptor fd = is.getFD();

    FileInputStream is1 = new FileInputStream(fd);
    BufferedInputStream br1 = new BufferedInputStream(is1);

    is.close();
    System.out.println(is1.read());

在上面的示例中,FileDescriptorfd在流isis1之间共享。对is进行关闭调用时,is1也会被关闭(基本上是关闭/释放fd)。
我的问题是,FileInputStream有一个标志来表示它是否关闭,但为什么当其他活动流指向它时,它会关闭FileDescriptor,从而使它们全部失败/无效,而不仅仅是关闭调用实例,并在没有其他流指向它时关闭FileDescriptor?

坦白地说,我认为这是因为FileDescriptor不应该像您现在使用它们一样被重复使用。 - Louis Wasserman
1个回答

1
如果您从一个File对象而不是FileDescriptor打开FileInputStreams,则会得到您期望的行为:
File f = new File( ... );
InputStream in1 = new FileInputStream( f );
InputStream in2 = new FileInputStream( f );

你将在FileInputStream(File)构造函数源代码中看到,它为每个FileInputStream创建一个新的FileDescriptor,而不是共享传入的一个,就像在FileInputStream(FileDescriptor)构造函数中发生的那样。
javadoc中可以看出,FileDescriptor包装了表示打开文件句柄的底层本地操作系统结构。 InputStream.close()的整个目的是清理这些本机资源。因此,如果两个InputStream共享本机文件句柄,则关闭一个后,另一个也会受到影响。
在我提供的另一个示例中,操作系统/本机级别创建了两个独立的文件句柄,因此它们可以独立关闭。

是的,如果我们明确打开文件,这些问题就不会发生。但我很困惑,为什么调用close会释放/关闭使用它的所有流,当文件描述符在这些流之间共享时,而不是只关闭一个实例。只是想知道背后是否有特定的原因? - k0der
我稍微修改了我的答案来解决这个问题。这是因为FileDescriptor所代表的是操作系统/本地文件句柄。所以你的两个InputStreams共享着这个与文件的操作系统级别/本地连接。如果你查看close()的JavaDoc,你会看到它的作用是关闭操作系统资源。 - ulmangt
明白了。感谢您的及时帮助。 - k0der

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