BufferedInputStream的read()方法源码解析

2022-06-30 11:38:39

BufferedInputSream默认是现实给出了一个8192byte大小的数组。

private static int DEFAULT_BUFFER_SIZE = 8192;

public BufferedInputStream(InputStream in) {
    this(in, DEFAULT_BUFFER_SIZE);
}

public BufferedInputStream(InputStream in, int size) {
    super(in);
    if (size <= 0) {
        throw new IllegalArgumentException("Buffer size <= 0");
    }
    buf = new byte[size];
}

在其中有一些参数用来表示缓存数组的存取情况。

protected int pos;
protected int count;

pos用来表示在当前数组中,被消费到的位置,正常情况下,当read()方法被调用时,被取出的数据就是buf[pos]位置上的数据。count则表示在缓存数组中可以被读取的数据大小,因此pos大小应该是介于0与count之间的,而count的大小应该是介于buf这个数组的长度上面的。

protected int markpos = -1;
protected int marklimit;

markpos表示添加书签的位置而marklimit则表示在读取过书签失效的最大位置。

在每次要根据buf来进行相关的操作的时候都要通过getBufIfOpen()来获得。

private byte[] getBufIfOpen() throws IOException {
    byte[] buffer = buf;
    if (buffer == null)
        throw new IOException("Stream closed");
    return buffer;
}

每次都根据buf的引用来确认是否为空,保证在多线程情况下,能够及时确认到stream的关闭。

通过read()来获取数据。

public synchronized int read() throws IOException {
    if (pos >= count) {
        fill();
        if (pos >= count)
            return -1;
    }
    return getBufIfOpen()[pos++] & 0xff;
}

根据前面对于pos和count的解释,当pos大于等于count的时候,证明buf数组中已经没有可以获取的数据,则需要fill()方法来扩充或者补充buf中的数据,如果经过fill()方法后,pos仍旧大于等于count,则代表stream已经被读完,则返回-1,代表已经被读完。如果经过fill()方法pos已经小于count或者本身pos已经小于count,那么直接返回buf[pos]上的数据,pos并自加准备取下一个位置上的数据。

但是取出来的数据需要和0xff相与,实则是为了保证表示读取完毕的Integer-1与正常读取的Byte-1进行区别。Integer类型的-1,则为32位1,而Byte形式的-1则是1111 1111,返回时如果直接转为Integer会直接变为-1,导致无法确认究竟结束还是真的读取数据为Byte为-1,所以讲Byte与0xff(也就是8位1)按位相与的结果再转化为Integer则是高24位为0,第八位为1,转化的结果则是255,避免和结束标志冲突。

fill()则是前面用来补充缓存数组中的数据的方法。

private void fill() throws IOException {
    byte[] buffer = getBufIfOpen();
    if (markpos < 0)
        pos = 0;            /* no mark: throw away the buffer */
    else if (pos >= buffer.length)  /* no room left in buffer */
        if (markpos > 0) {  /* can throw away early part of the buffer */
            int sz = pos - markpos;
            System.arraycopy(buffer, markpos, buffer, 0, sz);
            pos = sz;
            markpos = 0;
        } else if (buffer.length >= marklimit) {
            markpos = -1;   /* buffer got too big, invalidate mark */
            pos = 0;        /* drop buffer contents */
        } else if (buffer.length >= MAX_BUFFER_SIZE) {
            throw new OutOfMemoryError("Required array size too large");
        } else {            /* grow buffer */
            int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
                    pos * 2 : MAX_BUFFER_SIZE;
            if (nsz > marklimit)
                nsz = marklimit;
            byte nbuf[] = new byte[nsz];
            System.arraycopy(buffer, 0, nbuf, 0, pos);
            if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
                // Can't 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;
        }
    count = pos;
    int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
    if (n > 0)
        count = n + pos;
}

当进入fill()方法后,如果不存在markpos,则表示当前的缓存数组完全可以被替换掉,因此pos被置为0,准备从0开始到数组结束全部更新。此时,将会从给出的数据流中读取pos位置到缓存数组总长度大小的数据加入到缓存数组当中。而count,也就是可读取数据的最后一位,则为之前放入缓存数组的数据长度加上pos。

如果存在markpos的情况下,也就说当前缓存数组中的数据需要得到一定的保留。

如果pos已经大于等于缓存数组的大小,先确认markpos是否存在,如果大于0,则表明在markpos之前的数据可以被丢弃,那么直接就把markpos到pos的数据复制到缓存数组中,并将pos赋值到原本数组长度减去markpos的长度,以确保接下来的数据可以被正确读取,并在接下来继续填充数据,markpos也自然被赋值为0。

如果markpos已经为0,则确认当前数组的长度是否已经大于marklimit,则表明已经没有必要继续保持markpos,则赋值为-1,并pos赋值为0,准备将缓存数组全部重新填充数据。

如果并没有大于marklimit,则需要扩充数组以确保书签的成功保留,但是如果当前的数组大小已经最大大小,则会抛出错误。

如果并没有超出最大大小,则当前缓存数组可以继续扩容。

扩容时,当前pos的大小应该等于当前数组的大小,则选择2倍pos和最大数组大小中的较小者作为扩容后的数组大小。之后将原本数组的数据复制到新生成的较大数组,并通过cas确保线程安全的将新数组作为新的缓存数组复制给当前BufferedInputStream。完成之后,重新给新数组如同之前一样补充数据。

  • 作者:tydhot
  • 原文链接:https://blog.csdn.net/weixin_40318210/article/details/82194986
    更新时间:2022-06-30 11:38:39