Java 的 BufferedInputStream 幕后

Behind the scenes of Java's BufferedInputStream

首先,我理解缓冲作为包装器的概念,例如,FileInuptStream充当从底层流中读取内容(假设读取场景)的临时容器,在此案例 - FileInputStream.

  1. 比如说,有 100 个字节要从流(文件作为源)中读取。
  2. 没有缓冲,代码(BufferedInputStreamread 方法)必须进行 100 次读取(一次一个字节)。
  3. 使用缓冲,根据缓冲区大小,代码读取 <= 100 次。
  4. 假设缓冲区大小为 50。
  5. 因此,代码只读取缓冲区(作为源)两次以读取文件的内容。
  6. 现在,由于 FileInuptStream 是数据(包含 100 字节的文件)的最终来源(虽然被 BufferedInputStream 包装),它是否必须读取 100 次才能读取 100字节?虽然,代码调用了 BufferedInputStreamread 方法,但是调用被传递给了 FileInuptStreamread 方法,它需要进行 100 次读取调用。这是我无法理解的一点。

IOW,尽管被 BufferedInputStream 包装,底层流(例如 FileInputStream)仍然必须一次读取一个字节。那么,缓冲的好处在哪里(不是只需要两次读取缓冲的代码,而是应用程序的性能)?

谢谢。

编辑:

我将其作为后续 'edit' 而不是 'comment' 因为我认为它在上下文中更适合这里和作为 TL;DR 供 @Kayaman 和我之间聊天的读者使用.

BufferedInputStreamread方法说(节选):

As an additional convenience, it attempts to read as many bytes as possible by repeatedly invoking the read method of the underlying stream. This iterated read continues until one of the following conditions becomes true:

The specified number of bytes have been read,
The read method of the underlying stream returns -1, indicating end-of-file, or
The available method of the underlying stream returns zero, indicating that further input requests would block. 

我深入研究了代码,发现方法调用跟踪如下:

  1. BufferedInputStream -> read(byte b[]) 作为一个我想看到缓冲的作用。
  2. BufferedInputStream -> read(byte b[], int off, int len)
  3. BufferedInputStream -> read1(byte[] b, int off, int len) - 私人
  4. FileInputStream - 读取(字节 b[],int off,int len)
  5. FileInputStream -> readBytes(byte b[], int off, int len) - 私有和本地。源代码中的方法描述 -

Reads a subarray as a sequence of bytes.

BufferedInputStream 中对 read1(上面提到的#4)的调用处于无限 for 循环中。它 return 在上述 read 方法描述摘录中提到的条件下。

正如我在 OP(#6) 中提到的,该调用似乎确实由匹配 API 方法描述和方法调用跟踪的底层流处理。

问题仍然存在,如果本机 API 调用 - FileInputStreamreadBytes 一次读取一个字节并创建这些字节的数组到 return?

The underlying streams(such as FileInputStream) still have to read one byte at a time

幸运的是,不,那将是非常低效的。它允许 BufferedInputStreamFileInputStream 进行 read(byte[8192] buffer) 调用,这将 return 一大块数据。

如果您随后想要读取(或不读取)单个字节,它将有效地从 BufferedInputStream's 内部缓冲区 return 编辑,而不必深入到文件级别。所以 BI 是为了减少我们从文件系统实际读取的时间,当这些完成后,即使最终用户只想读取几个字节,它们也会以高效的方式完成。

从代码中可以清楚地看出,BufferedInputStream.read() 而不是 直接委托给 UnderlyingStream.read(),因为那样会绕过所有缓冲。

public synchronized int read() throws IOException {
    if (pos >= count) {
        fill();
        if (pos >= count)
            return -1;
    }
    return getBufIfOpen()[pos++] & 0xff;
}