Java缓冲流BufferedStream详解

前言

        适合人群:不用啥特别基础的小白

本文的写作目的在于我发现身边很多人对于缓冲流可能有误区,知道得不够全面,一般会有如下几个观点:

        1.缓冲流是用于包装其他流,达到缓冲加速的目的,性能要比普通流要好

        2.缓冲流利用了缓冲区,字节一次过读取到缓冲区中,只进行了一次磁盘IO,然后再从缓冲区读取数据,而普通流每读一个字节都需要进行一次磁盘IO,缓冲流大大降低了磁盘IO次数而达到了性能提高的效果。

原理

        实际上,以上两个观点都不太对,也不能说全错,大概都对了一半,先回答一下真实情况:

        1.缓冲流在大部分情况是要比普通流性能高,这个大部分情况可以粗略认为就是在于读取数据用的数组长度<8196的时候,而在数组长度>8196的情况下很可能性能反而更加低

        2.普通流读取数据确实要每个字节都要磁盘IO,但是缓冲流同样也要每个字节磁盘IO先读进缓冲区,然后再从缓冲区里读,并非说用了缓冲流就只需要一次磁盘IO

        缓冲流的具体实现其实是这样的:

         流程总结如下:

        (假设现在有缓冲流bufferedStream,它所包装的普通流为normalStream)

        1.缓冲区里初始化一个长度为8196的数组当做缓冲区buf

        2.读取数据时先调用normalStream.read(buf),用普通流把数据读进buf,这个步骤说明了缓冲流依然逃不开每读一个字节就需要进行一次IO

        3.调用System.arraycopy()方法把数据从缓冲区buf复制到加载数据的数组中

        4.另外:如果读取数据的数组长度>8196,那么将会不使用缓冲区,进行直接读取

        具体源码就没必要写出来了,在哪里可以看到呢,可以给大家指明一下:

        (现在拿BufferedInputStream为例,大家可以自行对比自己去看BufferedOutputStream,我是JDK1.8)

        1.流程第一段在183和203行的这一句buf = new byte[size]

        2.流程第二段在265和246行的fill()以及里面的getInIfOpen().read(buffer, pos, buffer.length - pos)

        3.流程第三段在291行的read1()方法里面的System.arraycopy(getBufIfOpen(), pos, b, off, cnt)

        4.流程第四段在282行的read1()方法里面的getInIfOpen().read(b, off, len)

证明

        其实上面从理论上已经证明了,现在从实际效果看一看,直接上代码:

public class BufferedStreamTest {
    public static void main(String[] args){
        File file = new File("C:\\Users\\Administrator\\Desktop\\test.txt");
        try {
            byte[] buf1 = new byte[128];
            byte[] buf2 = new byte[8196];
            byte[] buf3 = new byte[8196 * 1024];
            readData(file,buf1,false);
            readData(file,buf1,true);
            readData(file,buf2,false);
            readData(file,buf2,true);
            readData(file,buf3,false);
            readData(file,buf3,true);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static void readData(File file,byte[] buf,boolean isBuffered)throws Exception{
        FileInputStream fis = new FileInputStream(file);
        BufferedInputStream bfis = new BufferedInputStream(fis);
        InputStream inputStream = isBuffered?bfis:fis;
        long startTime = System.currentTimeMillis();
        while (inputStream.read(buf)!= -1){
        }
        System.out.println(inputStream+"--buf_length_"+buf.length+" 耗时为: "+(System.currentTimeMillis() - startTime));
        System.out.println("----------------------------------------------------");
    }
}

        注意点:

        1.这里读取的文件test.txt的大小在26MB,由于现在的计算机性能都很好,文件太小效果不明显

        2.自己做实验的时候,inputStream不要复用,一定要每一次都要新new的,不要前面已经读取完了,用同一个inputStream相当于没数据可读,耗时会是0

        3.为了消除计算机硬件层面的缓存的影响,我都是把普通流写在缓冲流前面,以消除缓冲流读完后因为有了缓存所以普通流才读得快的情况

        我们来看看结果:

         从结果就能看出来了:

        1.当数组长度小于8196,缓冲流明显更快

        2.当数组长度等于8196,缓冲流和普通流速度 相当(实际上缓冲流因为附加逻辑判断的原因还会更慢,图上结果缓冲流快2s只是因为计算机底层硬件缓存导致的)

        3.当数组长度大于8196,普通流是要快于缓冲流的,因为缓冲流多了许多附加逻辑的原因会有性能损耗,不过倒是很轻微的损耗

结论

        1.缓冲流不一定比普通流快,但是慢的时候也慢不了多少

        2.缓冲流也要进行多次磁盘IO,这取决于缓冲流所包装的普通流的read()方法的实现了,因为本质就是调用了所包装的普通流的read(buf)

        在实际开发建议多使用缓冲流没什么问题,它为程序员屏蔽了很多细节,让大家不用考虑太多就有一种“缓冲流可以加速”的体验感;当然了,如果经验比较丰富的话,而且对内存空间以及极限速度有更大追求的话,缓冲流就不一定是个好选择,可以根据需要去实现自己IO模型。

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值