字符流与字节流 buffer

字符流与字节流

java中所有IO操作都可以认为是流的操作

字符流与字节流 buffer_第1张图片
字符流与字节流 buffer_第2张图片

常用的类

  • befferedReader
    • 带有缓存char的fileReader
    • 入参为Rander子类, 然后在Reader基础上生成一个缓冲区域(默认8k char),提高IO效率
  • FileReader entexd InputStreamReader
    • 文件字符输入流
    • 入参为 File
    • 通过File直接创建FileInputStream流,作为输入字符流的来源 作为InputStreamReader的入参,然后根据默认字符集进行转码
    • 缺陷: 不能指定读入的字符流的编码
  • InputStreamReader
    • 输入字符流读取,在输入流的基础上增加了转码功能
  • FileInputStream
    • 文件输入流,inputStream子类
    • 从文件中读取输入流
  • BufferInputStream

    • 带有缓存的输入流
    • 先将IO流读取到缓存区中加速操作
  • FilterInputStream

    • 是BufferInputStream的父类
    • 没有什么实际的作用,据说更多地像是一个接口(不了解)

字符流与字节流使用了装饰者模式,如果需要一个带有缓存的文件字符输入流可以通过

bufferedReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(new FileInputStream("filePath"))));

bufferedReader = new BufferedReader(new FileReader("filePath"));

//这两种方式其实差不多 第二种字节流读取的时候没有缓存区,可能会慢一些,而且FileReader不可以设置编码。
  • 最底层是一个 文件字节流,包裹一个带缓存的字节流,通过InputStreamReader转码,再通过BufferedReader保存缓存的字符。
    个人十分好奇然后又了解了一下buffer是怎么实现加速的
  • 找到了一个10年的博客
https://blog.csdn.net/zealot_2002/article/details/8231194

据我了解,运用FileInputStream读写一段数据是一个字节一个字节的读取,
如果有10个字节大小的文件,就要调用10次系统调用,每次将读取的数据赋值给变量,
然后程序使用变量。缓冲区可以看作是一个放在内存中的数组,但是从硬盘中读取数据
仍然要使用系统调用,系统调用的读取仍然是每次一个,只是每调用一次之后,将所得
到的数据放入缓冲区中的,然后程序一次性使用10个数据。
我是这样理解的,但是不管用与不用缓冲区,使用的系统调用是一样多的。
(不知我的理解正确与否,请指出)如果我理解的是正确的,那么为什么使用缓冲区
读写的效率要高呢??谢谢!

满意回答
2010-11-21 17:31
理解是对的。
调用I\O操作的时候,实际上还是一个一个的读或者写,关键就在,CPU只有一个,
不论是几个核心。CPU在系统调用时,会不会还要参与主要操作?参与多次就会花更多的时间。

系统调用时,若不用缓冲,CPU会酌情考虑使用
中断。此时CPU是主动地,每个周期中都要花去一部分去询问I\O设备是否读完数据,
这段时间CPU不能做任何其他的事情(至少负责执行这段模块的核不能)。
所以,调用一次读了一个字,通报一次,CPU腾出时间处理一次。

而设置缓冲,CPU通常会使用 DMA 方式去执行 I\O 操作。CPU
将这个工作交给DMA控制器来做,自己腾出时间做其他的事,当DMA完成工作时,
DMA会主动告诉CPU“操作完成”。这时,CPU接管后续工作。在此,CPU 是被动的。
DMA是专门 做 IO 与 内存数据交换的,不仅自身效率高,也节约了CPU时间,
CPU在DMA开始和结束时做了一些设置罢了。
所以,调用一次,不必通报CPU,等缓冲区满了,DMA 会对CPU 说
“嘿,伙计!快过来看看,把他们都搬走吧”。

综上,设置缓冲,就建立了数据块,使得DMA执行更方便,CPU也有空闲,而不是呆呆地候着I\O数据读来。从微观角度来说,设置缓冲效率要高很多。尽管,不能从这个程序上看出来。 几万字的读写\就能看到差距

这个回答涉及了底层硬件DMA的知识,我不太了解底层是否真的会这样去做,
根据java BufferedInputStream的源码

    BufferedInputStream.java

    /**
     * Fills the buffer with more data, taking into account
     * shuffling and other tricks for dealing with marks.
     * Assumes that it is being called by a synchronized method.
     * This method also assumes that all data has already been read in,
     * hence pos > count.
     */
    private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();
        if (markpos < 0)
            pos = 0;            /* no mark: throw away the buffer */
       //删除了markpos不为0的处理
        count = pos;
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            count = n + pos;
    }

它调用了传入字节流的read方法,然后将数据保存到buffer对象中去

    FileInputStream.javax

    private native int readBytes(byte b[], int off, int len) throws IOException;

    public int read(byte b[]) throws IOException {
        return readBytes(b, 0, b.length);
    }

    public int read(byte b[], int off, int len) throws IOException {
        return readBytes(b, off, len);
    }
  • FileInputStream中的read调用native方法,入参是我们的 (buffer数组,off,len)
  • 结合上下我只能认为这个native方法中会实现DMA操作,
    但是这个效率和我们直接访问FileInputStream方法的结果是一样的,
    他的效率提升可能是通过传入buffer较大,可以一次性地将大量IO集中起来完成(在一块存储单元中)
    当我们后续使用 单字符,或较短字符数组 的读取方式时就不需要再去继续IO,提高效率
  • emmm 应该是这样
最后 有兴趣的可以去来接一下DMA是什么,硬件其实也是很神奇的东西哈

你可能感兴趣的:(java)