输出流与缓冲输出流



在Java 8中,两者之间有什么真正的区别吗:

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

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

我读了这个如此的问题和答案,但它让我很困惑。

PS:Java 11 中有什么变化?

如本答案中所述,缓冲流应该减少系统调用的数量。仅当应用程序发出大量小的读取或写入请求,导致大量系统调用时,这才有意义。这就是链接答案所说的"低效"的含义。

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

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

但是,如果应用程序的缓冲区仅略少,则缓冲流将完成其缓冲工作,从而引入额外的复制开销。但如前所述,只有当您实际保存系统调用并且根据体系结构的不同,您可能不得不说"......如果您实际上节省了大量系统调用"。较大的缓冲区本身并不是改进。

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

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

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

因此,缓冲流并不总是更有效;在某些情况下,缓冲甚至可能降低性能。此外,有时应用程序对最高性能不感兴趣,而是对及时写入基础媒体感兴趣。包装OutputStream,在需要时进行缓冲比选择退出缓冲更容易,如果流已经是BufferedOutputStream

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

不同之处在于,每次给底层系统一个字节进行写入时,未缓冲的输出流都会对底层系统进行写入调用,而缓冲的输出流将要写入的数据存储在缓冲区中,使系统调用仅在调用 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

最新更新