BufferedInputStream的read()函数和fill()函数

📅 2026/6/30 2:00:41
BufferedInputStream的read()函数和fill()函数
public class BufferedInputStream extends FilterInputStream { ...省略部分代码... // 默认缓冲区byte[]数组大小为8192 字节8KB private static int DEFAULT_BUFFER_SIZE 8192; // 最大缓冲区byte[]数组大小为2147483639byte大约2GB左右 private static int MAX_BUFFER_SIZE Integer.MAX_VALUE - 8; //缓冲区数组用volatile修饰是为了通过AtomicReferenceFieldUpdater进行CAS更新时保证内存的可见性 protected volatile byte buf[]; //底层是通过反射找到目标字段的内存偏移量然后利用Unsafe.class提供的CASCompare-And-Swap操作来原子地更新某个类中指定变量的值 private static final AtomicReferenceFieldUpdaterBufferedInputStream, byte[] bufUpdater AtomicReferenceFieldUpdater.newUpdater (BufferedInputStream.class, byte[].class, buf); //缓冲区byte[]数组中有效字的节数数量 protected int count; //准备从缓冲区中byte[]数组读取的字节索引位置取值范围为0poscount protected int pos; //在缓冲区byte[]数组中标记的某个索引位置-1markpospos //该变量只会在 fill()函数和mark()函数中赋值 protected int markpos -1; // 标记后最多可读取字节数量该变量只会在 mark()函数中赋值 //每当pos-markposmarklimit时就会设置markpos-1 来删除标记 protected int marklimit; //如果缓冲区byte[]数组不为空则返回该缓冲区byte[]数组否则抛出异常 private byte[] getBufIfOpen() throws IOException { byte[] buffer buf; if (buffer null) throw new IOException(Stream closed); return buffer; } private void fill() throws IOException { byte[] buffer getBufIfOpen();//获取缓冲区byte[]数组 if (markpos 0)//如果还没有调用过mark()函数那么markpos-1 pos 0;//pos0可以从缓冲区byte[]数组的索引0的位置开始读字节了 else if (pos buffer.length) if (markpos 0) { //场景一pos缓冲区byte[]数组的长度并且markpos 0 int sz pos - markpos; //只把缓冲区byte[]数组中[markpos,pos) 索引区间的元素复制到缓冲区byte[]数组[0,pos-markpos索引区间 System.arraycopy(buffer, markpos, buffer, 0, sz); pos sz;//设置pospos-markpos markpos 0;//设置markpos0 } else if (buffer.length marklimit) {//场景二pos缓冲区byte[]数组的长度并且缓冲区byte[]数组的长度 marklimit markpos -1; //设置markpos -1 pos 0; //设置pos 0 } else if (buffer.length MAX_BUFFER_SIZE) {//场景三pos缓冲区byte[]数组的长度并且缓冲区byte[]数组的长度 2147483639 throw new OutOfMemoryError(Required array size too large); } else {//场景四pos缓冲区byte[]数组的长度并且不满足场景一、二、三时将缓冲区byte[]数组扩容 //如果pos2147483639/2则新缓冲区byte[]数组的长度nszpos*2否则新缓冲区byte[]数组的长度nsz2147483639 int nsz (pos MAX_BUFFER_SIZE - pos) ? pos * 2 : MAX_BUFFER_SIZE; if (nsz marklimit) nsz marklimit;//当新缓冲区byte[]数组的长度nszmarklimit新缓冲区byte[]数组的长度nszmarklimit byte nbuf[] new byte[nsz];//新建一个新缓冲区byte[]数组 System.arraycopy(buffer, 0, nbuf, 0, pos);//将老缓冲区byte[]数组中[0,pos)索引区间的元素复制到新缓冲区byte[]数组[0,pos)索引区间 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {//通过CASCompare-And-Swap操作来原子地更新buf变量 // Cant replace buf if there was an async close. // Note: This would need to be changed if fill() // is ever made accessible to multiple threads. // But for now, the only way CAS can fail is via close. // assert buf null; throw new IOException(Stream closed); } buffer nbuf;//新缓冲区byte[]数组创建完毕 } count pos; //将被装饰的输入Stream中的字节读取到缓冲区byte[]数组的[pos,buffer.length - pos)索引位置并返回读取的字节数量 int n getInIfOpen().read(buffer, pos, buffer.length - pos); if (n 0) count n pos;//count从被装饰的输入Stream中读取的字节数量pos } //线程同步的函数从缓冲区byte[]数组中读取1个字节 public synchronized int read() throws IOException { //poscount有2种情况pos不可能count //场景一poscount0缓冲区byte[]数组还没有填充任何数据。 //场景二poscount≠0缓冲区byte[]数组中的数据已经通过pos读取完了。 if (pos count) { fill();//符合场景一或场景二都会调用fill()函数 if (pos count) return -1;//如果调用了fill()函数后仍然符合场景一或场景二表示被装饰的输入Stream已经读完了返回-1 } //执行到这里时表明pos count返回缓冲区byte[]数组pos索引位置的字节; return getBufIfOpen()[pos] 0xff; } ...省略部分代码... }如果使用者用的是默认的构造函数创建了BufferedInputStream的对象如下所示伪代码InputStream is new FileInputStream(D:\\nio-data.txt); BufferedInputStream bis new BufferedInputStream(is);那么BufferedInputStream对象中的缓冲区byte[]数组的长度为8192缓存8KB字节如果此时执行BufferedInputStream.class::read()函数bis.read();过程如下假设被装饰的输入StreamFileInputStream中有10000个字节①、poscount0缓冲区byte[]数组中还没有填充任何数据执行fill()函数然后将被装饰的输入StreamFileInputStream中的字节读取到缓冲区byte[]数组的[0,8192)索引位置左闭右开不包括byte[]数组的第8192个索引位置并返回读取的字节数量。如下所示此时被装饰的输入StreamFileInputStream中的字节和缓冲区byte[]数组中的字节如下所示②、更新int count变量fill()函数中getInIfOpen().read(buffer, pos, buffer.length - pos)这行代码的返回值为8192count8192pos8192③、执行getBufIfOpen()[pos] 0xff从缓冲区byte[]数组中获取第pos此时pos0个索引位置的字节返回给BufferedInputStream.class::read()函数的调用方并更新pos的值为pos1之后每次调用BufferedInputStream.class::read()函数时都不会再执行fill()函数了直到pos8192时执行BufferedInputStream.class::read()函数才会再次执行fill()函数新的填充缓冲区byte[]数组的过程如下①、更新pos0count0缓冲区byte[]数组中是上一次执行fill()函数填充的从被装饰的输入StreamFileInputStream读取的第18192个字节本次需要将被装饰的输入StreamFileInputStream中的第819310000个字节读取到缓冲区byte[]数组的[0,1808)索引位置左闭右开不包括byte[]数组的第1808个索引位置并返回读取的字节数量。如下所示此时被装饰的输入StreamFileInputStream中的字节被全部读取完毕被装饰的输入StreamFileInputStream为空。②、更新int count变量fill()函数中getInIfOpen().read(buffer, pos, buffer.length - pos)这行代码的返回值为1808count1808pos1808③、执行getBufIfOpen()[pos] 0xff从缓冲区byte[]数组中获取第pos此时pos0个索引位置的字节返回给BufferedInputStream.class::read()函数的调用方并更新pos的值为pos1之后每次调用BufferedInputStream.class::read()函数时都不会再执行fill()函数了直到pos1808时执行BufferedInputStream.class::read()函数才会再次执行fill()函数过程如下①、更新pos0count0缓冲区byte[]数组中的数据如下此时由于被装饰的输入StreamFileInputStream中的字节被全部读取完毕fill()函数中getInIfOpen().read(buffer, pos, buffer.length - pos)这行代码的返回值为0无法更新count结束fill()函数的调用②、执行return -1返回给BufferedInputStream.class::read()函数的调用方3.1.1、如果在多次执行BufferedInputStream.class::read()函数之前执行过mark()函数标题3.1分析了很多次只调用read()函数之后最后缓冲区byte[]数组中的字节内容并没有分析很多次调用read()函数之前很多次调用read()函数之中很多次调用read()函数之后分别调用了mark()函数和reset()函数的场景。如果在很多次调用read()函数之前调用了mark(8192)函数如下伪代码InputStream is new FileInputStream(D:\\nio-data.txt); BufferedInputStream bis new BufferedInputStream(is); bis.mark(8192);//设置marklimit8192markpospos0 int bytesRead; while ((bytesRead bis.read()) ! -1) { //处理读取到的字节bytesRead }那么BufferedInputStream对象中的缓冲区byte[]数组的长度为8192缓存8KB字节如果此时如上述代码一样在while循环中执行BufferedInputStream.class::read()函数过程如下假设被装饰的输入StreamFileInputStream中有10000个字节①、poscount0缓冲区byte[]数组中还没有填充任何数据执行fill()函数然后将被装饰的输入StreamFileInputStream中的字节读取到缓冲区byte[]数组的[0,8192)索引位置左闭右开不包括byte[]数组的第8192个索引位置并返回读取的字节数量。如下所示此时被装饰的输入StreamFileInputStream中的字节和缓冲区byte[]数组中的字节如下所示②、更新int count变量fill()函数中getInIfOpen().read(buffer, pos, buffer.length - pos)这行代码的返回值为8192count8192pos8192③、执行getBufIfOpen()[pos] 0xff从缓冲区byte[]数组中获取第pos此时pos0个索引位置的字节返回给BufferedInputStream.class::read()函数的调用方并更新pos的值为pos1之后每次调用BufferedInputStream.class::read()函数时都不会再执行fill()函数了直到pos8192时执行BufferedInputStream.class::read()函数才会再次执行fill()函数新的填充缓冲区byte[]数组的过程如下①、此时因为buffer.length marklimit所以更新markpos-1pos0后续的步骤与标题3.1相同。最终缓冲区byte[]数组中的数据如下最终pos0count0markpos-13.1.2、如果在多次执行BufferedInputStream.class::read()函数之中执行过mark()函数如果在很多次调用read()函数之中调用了mark(8192)函数如下伪代码InputStream is new FileInputStream(D:\\nio-data.txt); BufferedInputStream bis new BufferedInputStream(is); int bytesRead; int i 0; while ((bytesRead bis.read()) ! -1) { if(i4096){ bis.mark(8192);//设置marklimit8192markpospos4096 } //处理读取到的字节bytesRead }那么BufferedInputStream对象中的缓冲区byte[]数组的长度为8192缓存8KB字节上述代码的执行过程如下假设被装饰的输入StreamFileInputStream中有20000个字节①、poscount0缓冲区byte[]数组中还没有填充任何数据执行fill()函数然后将被装饰的输入StreamFileInputStream中的字节读取到缓冲区byte[]数组的[0,8192)索引位置左闭右开不包括byte[]数组的第8192个索引位置并返回读取的字节数量。如下所示此时被装饰的输入StreamFileInputStream中的字节和缓冲区byte[]数组中的字节如下所示