在Java 8中,两者之间是否有真正的区别?
try (OutputStream os = Files.newOutputStream(path)) {
[...]
}
和
try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(path))) {
[...]
}
我读了这个SO question and answers,但是这让我很困惑。
PS:Java 11中有什么变化吗?
答案 0 :(得分:3)
如this answer中所述,缓冲流应该减少系统调用的次数。仅当应用程序发出大量小的读取或写入请求并导致大量系统调用时,这才有意义。这就是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字节的缓冲区。由于此缓冲流不知道您总共要写入多少,因此它将把请求数据复制到其缓冲区中,因为缓冲区较小,因此在关闭操作期间将其刷新(写入)。因此,最后,您无需保存任何系统调用,但可以节省复制开销。
因此,缓冲流并不一定总是效率更高;在某些情况下,缓冲甚至可能会降低性能。同样,有时应用程序对最高性能不感兴趣,但对及时写入基础媒体不感兴趣。如果流已经是OutputStream
,则包装BufferedOutputStream
以便在需要时获得缓冲比退出缓冲要容易得多。
当您查看JDK 1.4中引入的NIO Channel API时,您会发现没有缓冲通道。相反,它不提供用于读取或写入单个字节的方法,而且,它迫使程序员使用ByteBuffer
来指导他们分离I / O和数据处理。这是首选的方式。
答案 1 :(得分: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