深入解析Java中Flushable接口的flush方法

本文探讨了flush方法在操作系统中的作用及其实现原理。通过对比不同的写入策略,解释了何时及为何需要调用flush方法来确保数据立即写入磁盘。此外,还讨论了频繁调用flush可能带来的性能影响。

今天写这篇文章是为了纪念同事讲得两句话:1、调用flush方法等于在后面对OutputStream使劲的抽一鞭子,并命令“赶紧给我写入,我的水桶太满了”;2、写入数据量不大时,可以考虑不用。

先来说说flush方法为了解决什么问题。我们都知道在Linux中,可写的句柄都是”文件“,并且,不管是Windows还是Linux都有提供相同名字的flush系统调用,而且操作系统在写文件时,先把要写的内容从用户缓冲区复制到内核缓冲区等待真正的写入到“文件”。java中的Flushable.flush()方法显然也是调用操作系统提供的接口。不管怎么调用,他们的原理都是一样的,比如要写4K大小的文件,操作系统有几种策略把字节写入到”文件“中:1、应用程序每写一个字节,操作系统马上把这个字节写入”文件“。2、应用程序写入字节后,操作系统不马上写入,而是先把它缓存起来,到达一定数量时才写入”文件“。3、应用程序写入字节后,没有到达可写的字节数量时,操作系统不写入,而是由应用程序控制。

我们先来看第一种策略,这种策略对操作系统来讲,显然效率太低,不可取。第二种策略,为了弥补第一种策略的不足,达到一定数量时才写入,可以提高系统利用率,现代大部分操作系统也都这样实现的。那么问题来了,当写入一定数量的字节后,虽然还没有达到操作系统可写入的数量,但是应用程序有这个需求说,我得马上写入。那怎么办?为了应对这种策略,操作系统提供了flush系统调用,让应用程序可以控制何时马上写入文件。这也是第三种策略。

说到这里,有的人可能有疑问,那应用程序写入字节数不足以达到操作系统要写入的数量,而且没有调用flush方法,那这些字节是不是就丢失了?答案是否定的,当打开一个文件句柄,不管写入多少字节的内容,在调用close方法时,系统会自动写入未写的内容,很多操作系统的close方法实现中就有调用flush方法的部分。

此时我们再回过头来看看同事讲的两句话。第一句话的前半句是对的,至于“我的水桶太满了”,同事的意思是说,调用flush方法就是为了水桶太满,这显然违背事实的。水桶达到一定高度时操作系统会排光水而空出桶的空间以备继续接收水。至于第二句话,如果数据量不大,而急需把内容写到“文件”中,此时,必须调用flush方法,除非close掉文件句柄。

最后要注意一点,当操作系统内核缓冲区中还有未写入的字节,而此时系统奔溃或者断电等情况,那么这部分内容也就丢失了。所以要不要调用flush方法,要看具体的需求,笔者认为大部分时候没有必要调用flush方法。频繁的调用flush方法会降低系统性能,举个极端的例子,每写入一个字节就调用一次,这显然就退化到了上面提到的第一种策略。

### 流的介绍 在 Java 中,流是用于在数据源(如文件、网络连接、内存等)和程序之间传输数据的抽象概念。四种基本流包括 InputStream、OutputStream、Reader 和 Writer,它们又分别有更具体的子类,分为文件流、缓冲流、数据流、转换流、Print 流、Object 流等,都分别有特定的功能或用来操作特定的数据 [^2]。InputStream 抽象类实现了 Closeable 接口,Closeable 接口上面还有一个 AutoCloseable 接口;OutputStream 抽象类实现了 Closeable 接口Flushable 接口;Reader 抽象类实现了 Closeable 接口和 Readable 接口;Writer 抽象类实现了 Appendable 接口Flushable 接口和 Closeable 接口 [^4]。 ### 缓冲流的介绍 缓冲流是对基本流的一种包装,通过设置缓冲流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用。例如,BufferedOutputStream 实现缓冲输出流,BufferedInputStream 创建时会创建一个内部缓冲区数组,当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节 [^1]。 ### 流和缓冲流的使用方法 以下是使用文件流和缓冲流进行文件读写的示例代码: ```java import java.io.*; public class StreamExample { public static void main(String[] args) { try { // 使用文件流写入文件 FileOutputStream fos = new FileOutputStream("test.txt"); fos.write("Hello, World!".getBytes()); fos.close(); // 使用文件流读取文件 FileInputStream fis = new FileInputStream("test.txt"); int data; while ((data = fis.read()) != -1) { System.out.print((char) data); } fis.close(); // 使用缓冲流写入文件 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("test2.txt")); bos.write("Hello, Buffered World!".getBytes()); bos.close(); // 使用缓冲流读取文件 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("test2.txt")); int bufferedData; while ((bufferedData = bis.read()) != -1) { System.out.print((char) bufferedData); } bis.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` ### 流和缓冲流的原理 可以用去水房打水的例子来理解流和缓冲流的原理。用节点流打水相当于每次取一滴水,需要进行多次才能完成,大部分的时间都浪费到路上了;用缓冲区相当于拿了一个小容器,每次可以取多滴水,效率明显提高了;而用高级流相当于拿了一个容量更大的容器取水,是效率最高的方式 [^3]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值