@author ixenos

 

 

 

概念


BufferedInput/OutputStream是实现缓存的过滤流,他们分别是FilterInput/OutputStream的子类。

 

BufferedInputStream工作流程


stream-->buf--read buf-->I

 

1.当一个BufferedInputStream被创建时,一个内部的缓冲区 byte[] buf = new byte[size] 也被建立,size默认是8192也就是默认创建一个8kb大小的缓存空间,

工作时由内部方法fill()预先在缓冲区存储来自连接输入流的数据最终的数据来源由底层流决定,比如new FileInputStream("a.txt")来源就是a.txt,而我们过滤流的数据来源是节点流!

 

 1     private static int DEFAULT_BUFFER_SIZE = 8192; //默认缓冲数组长度大小,折合单位有8kb
 2     protected volatile byte buf[]; //底层维护的缓冲数组变量
 3 
 4     public BufferedInputStream(InputStream in) {
 5         this(in, DEFAULT_BUFFER_SIZE); //默认8192
 6     }
 7 
 8 
 9     public BufferedInputStream(InputStream in, int size) {
10         super(in); //引用基类FilterInputStream的构造方法,基类有底层流变量
11         if (size <= 0) {
12             throw new IllegalArgumentException("Buffer size <= 0");
13         }
14         buf = new byte[size]; //初始化缓冲数组,指定大小为size
15     }

 

2.当BufferedInputStream的read方法被调用时,数据将从缓冲区中移出,而不是底层的输入流;

 

3.当BufferedInputStream缓冲区数据用完时,他自动从底层输入流中补充数据

 

  read()源码分析:

    //返回下一个数据字节,如果到达流末尾则返回-1,数据流向返回值!
1
public synchronized int read() throws IOException { 2 if (pos >= count) { //当缓存指针等于或者超过count时,说明buf已满 3 fill(); //fill()从节点流中读取数据存放到buf缓冲区 4 if (pos >= count) 5 return -1; 6 } 7 return getBufIfOpen()[pos++] & 0xff; //得到非空buf缓冲当前索引为pos位置的一字节数据,pos++ 缓冲区指针移动一位 8 } 9 10 //getBufIfOpen对buf进行非空判断 11 private byte[] getBufIfOpen() throws IOException { 12 byte[] buffer = buf; //用一个引用变量引用buf 13 if (buffer == null) 14 throw new IOException("Stream closed"); 15 return buffer; //返回buffer,实质上指向buf 16 }

 

  read(byte[], int, int)源码分析 :

//pos是起始/下一次读取buffer的位置,
//markpos是最新一次已读取的位置,
//count是缓冲数组被填充的大小-1(因为是作为index存在的,从0开始)
//len是b要读取的最大字节数
  1     //返回读取的字节数,如果流到达末尾返回-1,数据流向b数组!
  2     public synchronized int read(byte b[], int off, int len)
  3         throws IOException
  4     {
  5         
  6         /*这里只做检查,流关闭则提前抛出,以避免拖到fill()中的getBufIfOpen()再抛出的情况
  7            private byte[] getBufIfOpen() throws IOException {
  8                byte[] buffer = buf; //引用传递,指向当前buf变量指向的内存
  9                if (buffer == null) //就为了这个非空判断而已
 10                    throw new IOException("Stream closed");
 11                return buffer; //这里返回值是没人接收的,方法结束抛弃局部变量buffer,不对buf造成影响
 12            }
 13          */
 14         getBufIfOpen(); // Check for closed stream 关闭流将抛出异常
 15         //指定数组的越界判断
 16         if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
 17             throw new IndexOutOfBoundsException();
 18         } else if (len == 0) {
 19             return 0;
 20         }
 21 
 22         int n = 0;
 23         //循环写数据到b,直到buffer数据不足返回-1,此时nread已累加记录
 24         for (;;) {
 25             int nread = read1(b, off + n, len - n);//读缓冲区数据到b
 26             if (nread <= 0)
 27                 return (n == 0) ? nread : n;
 28             n += nread;
 29             //读完指定长度时返回
 30             if (n >= len)
 31                 return n;
 32             // if not closed but no bytes available, return
 33             //没读完指定长度,但缓冲区没数据时返回当前n
 34             InputStream input = in;
 35             if (input != null && input.available() <= 0)
 36                 return n;
 37         }
 38     }
 39 
 40 
 41     /**
 42      * read1方法是该read底层用来读取缓冲区buf的数据进指定数组的方法
 43      * 而fill方法又是read1方法中用来读取底层字节流到缓冲区buf的方法
 44      * 读取数据写进指定数组的一部分,必要时每次从底层流尽量读取数据
 45      */
 46     private int read1(byte[] b, int off, int len) throws IOException {
 47         int avail = count - pos; //开始时为0
 48         if (avail <= 0) {
 49             /* 如果所请求的长度大于等于缓冲区,并且还没读取过buf
50         (即刚开始使用时,扩充缓冲区成本不值得,回归底层流),
51 就没必要用缓冲区了,将直接从底层流读取数据。
52 */ 53 if (len >= getBufIfOpen().length && markpos < 0) { 54 //直接调用底层流的read方法,写进指定的内存b 55 return getInIfOpen().read(b, off, len); 56 } 57 //请求fill()方法来读取底层流数据(多次使用后如果长度大于缓冲区,为了数据保护,将扩充缓冲区) 58 fill(); 59 avail = count - pos; //avail是 60 if (avail <= 0) return -1; 61 } 62 int cnt = (avail < len) ? avail : len; //cnt是要写入b的字节数,如果b中剩余字节数avail比要刷入的数据长度len大,则cnt为len 63 System.arraycopy(getBufIfOpen(), pos, b, off, cnt); //复制指定范围非空buf数据到b 64 pos += cnt; 65 return cnt; 66 } 67 68 69 //fill()方法填充空间,是底层用来读取流数据到缓冲区buf 70 /** 71 * Fills the buffer with more data, taking into account 72 * shuffling and other tricks for dealing with marks. 73 * Assumes that it is being called by a synchronized method. 74 * This method also assumes that all data has already been read in, 75 * hence pos > count. 76 */ 77 private void fill() throws IOException { 78 byte[] buffer = getBufIfOpen(); 79 //pos是起始/下一次读取buffer的位置,markpos是最新一次已读取的位置,count是缓冲数组被填充的大小-1(因为是作为index存在的,从0开始) 80 //起始小于零表明没有pos移动,从源码可知 81 if (markpos < 0) 82 pos = 0; /* no mark: throw away the buffer */ 83 //当pos比buffer更长时,即 84 else if (pos >= buffer.length) /* no room left in buffer */ 85 //markpos也有前移时,数组自我复制丢弃早期部分 86 if (markpos > 0) { /* can throw away early part of the buffer */ 87 int sz = pos - markpos; 88 System.arraycopy(buffer, markpos, buffer, 0, sz); 89 pos = sz; 90 markpos = 0; 91 //buffer大小超过理论规模时,重置,通过改变pos“丢弃”缓冲区内容 92 } else if (buffer.length >= marklimit) { 93 markpos = -1; /* buffer got too big, invalidate mark */ 94 pos = 0; /* drop buffer contents */ 95 //buffer大小超过本地VM内存限制:MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8 96 } else if (buffer.length >= MAX_BUFFER_SIZE) { 97 throw new OutOfMemoryError("Required array size too large"); 98 } else { /* grow buffer */ //没有顾忌时,扩充缓冲区大小 99 int nsz = (pos <= MAX_BUFFER_SIZE - pos) ? 100 pos * 2 : MAX_BUFFER_SIZE; 101 if (nsz > marklimit) 102 nsz = marklimit; 103 byte nbuf[] = new byte[nsz]; 104 System.arraycopy(buffer, 0, nbuf, 0, pos); 105 /*AtomicReferenceFieldUpdater是一个基于反射的工具类, 106 它能对指定类的指定的volatile引用字段进行原子更新。 107 (注意这个字段不能是private的) , 108 从源码知getBufIfOpen返回的是值传递的protected volatile byte buf[] 109 */ 110 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { 111 // Can't replace buf if there was an async close. 112 // Note: This would need to be changed if fill() 113 // is ever made accessible to multiple threads. 114 // But for now, the only way CAS can fail is via close. 115 // assert buf == null; 116 throw new IOException("Stream closed"); 117 } 118 buffer = nbuf; 119 } 120 //pos小于buf.length时,从底层流到缓冲区,使用InputStream的read(byte[])方法 121 count = pos; 122 int n = getInIfOpen().read(buffer, pos, buffer.length - pos); 123 if (n > 0) 124 count = n + pos; 125 } 126

 

总结:通过以上源码我们可以看到:

  1)read(byte[])方法运行初始就用getBufIfOpen判断流关闭没有,因为缓冲流关闭后缓冲区失效,getBufIfOpen将抛出异常;

  2)read(byte[])方法中的read1方法是该read底层,用来读取缓冲区buf的数据进指定数组的方法,而fill方法又是read1方法中,用来读取底层字节流到缓冲区buf的方法,层层封装外包

  3)read(byte[]) <-- read1(byte[]) <-- fill() <-- getInIfOpen().read(buf) <-- InputStream 层层外包,

    这里getInIfOpen()返回一个非空底层流InputStream,并调用底层流的read(byte[])方法读取流数据到缓冲区buf,

    所以当我们只是关闭底层流这些嵌套流也跟着“关闭”,但是会留下缓冲数组buf,这有时可以利用,有时要注意关闭避免浪费资源;

     以下是close()的源码分析:

 1  1     public void close() throws IOException {
 2  2         byte[] buffer;
 3  3         while ( (buffer = buf) != null) {
 4  4             if (bufUpdater.compareAndSet(this, buffer, null)) { // 会将缓冲区buf的数据清空并释放缓冲区,也就是buf=null
 5  5                 InputStream input = in; //引用传递,input和in同指向节点流对象
 6  6                 in = null; //修改强引用,使底层流引用变量失效,即任何人都不能通过in去引用底层流了
 7  7                 if (input != null) //然后交给input单独来非空判断和执行关闭操作
 8  8                     input.close(); //调用节点流的close(),和native相关
 9  9                 return;
10 10             }
11 11             // Else retry in case a new buf was CASed in fill()
12 12         }
13 13     }
View Code

相关文章:

  • 2022-12-23
  • 2021-05-22
  • 2022-12-23
  • 2022-12-23
  • 2021-07-07
  • 2021-11-20
  • 2022-12-23
猜你喜欢
  • 2021-12-13
  • 2021-12-09
  • 2021-06-24
  • 2021-07-09
  • 2021-09-05
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案