OutputStream 与 BufferedOutputStream

OutputStream vs BufferedOutputStream

在java8中,有什么真正的区别吗:

try (OutputStream os = Files.newOutputStream(path)) {
    [...]
}

try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(path))) {
    [...]
}

我读了这个SO question and answers,但它让我很困惑。

PS: Java 11 有什么变化?

不同之处在于,每次您给它一个要写入的字节时,无缓冲都会对底层系统进行写入调用,而缓冲输出流会将要写入的数据存储在缓冲区中,从而使系统调用仅在调用 flush 命令后写入数据。这是为了通过减少 I/O 调用的操作来提高性能。

https://docs.oracle.com/javase/8/docs/api/java/io/BufferedOutputStream.html https://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html

正如, a buffered stream is supposed to reduce the number of system calls. This is only relevant, if the application makes a lot of small read or write requests, resulting in a lot of system calls. This is what the linked answer中所解释的,意思是“低效”。

通过使用可以通过一次调用读取或写入的大得多的缓冲区,并通过从缓冲区复制或复制到缓冲区来满足应用程序请求,您正在减少系统调用的数量。如果保存的系统调用比引入的复制开销更昂贵,这会提高性能。

所以使用缓冲流并不总是更好的原因是并非每个应用程序都发出如此小的请求。当应用程序发出合理大小的请求时,缓冲流可以做的最好的事情就是让路,所以如果它有一个空缓冲区并且应用程序发出与缓冲区大小相同甚至更大的请求,缓冲流会将请求直接传递给源流。

但是如果应用程序的缓冲区只是稍微少一点,缓冲流将完成其缓冲工作,引入额外的复制开销。但如前所述,如果您实际上节省了系统调用,您只会获得优势,并且根据体系结构,您可能不得不说,“……如果您实际上节省了大量系统调用”。更大的缓冲区本身并不是改进。

一个简单的例子就是,你只想将 1,000 字节写入一个文件,比如

byte[] data = /* something producing an array of 1,000 bytes */
try (OutputStream os = Files.newOutputStream(path)) {
    os.write(data);
}

因此,如果将输出流包装在 BufferedOutputStream 中,您将获得默认大小为 8192 字节的缓冲区。由于此缓冲流不知道您将总共写入多少,因此它将请求数据复制到其缓冲区,因为它较小,以便在关闭操作期间刷新(写入)。所以最后,你没有节省任何系统调用,而是得到了复制开销。

所以缓冲流并不总是更有效;在某些情况下,缓冲甚至可能会降低性能。此外,有时应用程序对最高性能不感兴趣,但对及时写入底层媒体感兴趣。如果流已经是 BufferedOutputStream.

,则包装 OutputStream 以便在需要时获得缓冲比选择退出缓冲更容易

当您查看 JDK 1.4 中引入的 NIO Channel API 时,您会注意到没有缓冲通道。相反,它不提供读取或写入单个字节的方法,此外,它强制程序员使用 ByteBuffer,引导他们分离 I/O 和处理数据。这是首选方式。