OutputStream vs BufferedOutputStream [重复]

问题描述 投票:0回答:2

这个问题在这里已有答案:

在java 8中,是否有任何真正的区别:

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

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

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

PS:Java 11中有些变化吗?

java file java-8 outputstream java-11
2个回答
2
投票

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


3
投票

正如在this answer中所解释的那样,缓冲流应该减少系统调用的数量。这只是相关的,如果应用程序发出很多小的读或写请求,导致很多系统调用。这就是linked answer所说的“效率低下”。

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

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

但是,如果应用程序的缓冲区仅略微减少,则缓冲流将完成其缓冲工作,从而引入额外的复制开销。但正如所说的那样,只有实际保存系统调用才能获得优势,并且根据架构,您可能不得不说,“......如果您实际节省了大量系统调用”。较大的缓冲区本身并不是一种改进。

一个简单的例子就是说,你只想写一个1000字节的文件,比如

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 Channel API时,您会注意到没有缓冲的通道。相反,它不提供读取或写入单个字节的方法,而且,它迫使程序员使用ByteBuffer来指导它们分离I / O和处理数据。这是首选方式。

© www.soinside.com 2019 - 2024. All rights reserved.