BufferedInputStream源码分析,解答readlimit相关的问题

承接上面一个主题:BufferedInputStream中,readlimit相关的问题

BufferedInputStream源码分析

这里写图片描述
BufferedInputStream类是一个装饰者子类,FilterInputStream为装饰者基类,InputStream类和其子类是被包装的类

原理图

原始InputStream读取方式,应用程序直接从物理设备上读取数据
这里写图片描述
BufferedInputStream读取方式,增加缓冲区数组,应用程序从数组中读取数据,减少与物理设备交互的次数
这里写图片描述

完整源代码

BufferedInputStream

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

public class BufferedInputStream extends FilterInputStream {
    private static int DEFAULT_BUFFER_SIZE = 8192;
    private static int MAX_BUFFER_SIZE = 2147483639;
    protected volatile byte[] buf;
    private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = AtomicReferenceFieldUpdater
            .newUpdater(BufferedInputStream.class, byte[].class, "buf");
    protected int count;
    protected int pos;
    protected int markpos = -1;
    protected int marklimit;

    private InputStream getInIfOpen() throws IOException {
        InputStream localInputStream = this.in;
        if (localInputStream == null) {
            throw new IOException("Stream closed");
        }
        return localInputStream;
    }

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

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

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

    private void fill() throws IOException {
        byte[] localObject = getBufIfOpen();
        if (this.markpos < 0) {
            this.pos = 0;
        } else if (this.pos >= localObject.length) {
            if (this.markpos > 0) {
                int i = this.pos - this.markpos;
                System.arraycopy(localObject, this.markpos, localObject, 0, i);
                this.pos = i;
                this.markpos = 0;
            } else if (localObject.length >= this.marklimit) {
                this.markpos = -1;
                this.pos = 0;
            } else {
                if (localObject.length >= MAX_BUFFER_SIZE) {
                    throw new OutOfMemoryError("Required array size too large");
                }
                int i = this.pos <= MAX_BUFFER_SIZE - this.pos ? this.pos * 2 : MAX_BUFFER_SIZE;
                if (i > this.marklimit) {
                    i = this.marklimit;
                }
                byte[] arrayOfByte = new byte[i];
                System.arraycopy(localObject, 0, arrayOfByte, 0, this.pos);
                if (!bufUpdater.compareAndSet(this, localObject, arrayOfByte)) {
                    throw new IOException("Stream closed");
                }
                localObject = arrayOfByte;
            }
        }
        this.count = this.pos;
        int i = getInIfOpen().read((byte[]) localObject, this.pos, localObject.length - this.pos);
        if (i > 0) {
            this.count = (i + this.pos);
        }
    }

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

    private int read1(byte[] paramArrayOfByte, int paramInt1, int paramInt2) throws IOException {
        int i = this.count - this.pos;
        if (i <= 0) {
            if ((paramInt2 >= getBufIfOpen().length) && (this.markpos < 0)) {
                return getInIfOpen().read(paramArrayOfByte, paramInt1, paramInt2);
            }
            fill();
            i = this.count - this.pos;
            if (i <= 0) {
                return -1;
            }
        }
        int j = i < paramInt2 ? i : paramInt2;
        System.arraycopy(getBufIfOpen(), this.pos, paramArrayOfByte, paramInt1, j);
        this.pos += j;
        return j;
    }

    public synchronized int read(byte[] paramArrayOfByte, int paramInt1, int paramInt2) throws IOException {
        getBufIfOpen();
        if ((paramInt1 | paramInt2 | paramInt1 + paramInt2 | paramArrayOfByte.length - (paramInt1 + paramInt2)) < 0) {
            throw new IndexOutOfBoundsException();
        }
        if (paramInt2 == 0) {
            return 0;
        }
        int i = 0;
        for (;;) {
            int j = read1(paramArrayOfByte, paramInt1 + i, paramInt2 - i);
            if (j <= 0) {
                return i == 0 ? j : i;
            }
            i += j;
            if (i >= paramInt2) {
                return i;
            }
            InputStream localInputStream = this.in;
            if ((localInputStream != null) && (localInputStream.available() <= 0)) {
                return i;
            }
        }
    }

    public synchronized long skip(long paramLong) throws IOException {
        getBufIfOpen();
        if (paramLong <= 0L) {
            return 0L;
        }
        long l1 = this.count - this.pos;
        if (l1 <= 0L) {
            if (this.markpos < 0) {
                return getInIfOpen().skip(paramLong);
            }
            fill();
            l1 = this.count - this.pos;
            if (l1 <= 0L) {
                return 0L;
            }
        }
        long l2 = l1 < paramLong ? l1 : paramLong;
        this.pos = ((int) (this.pos + l2));
        return l2;
    }

    public synchronized int available() throws IOException {
        int i = this.count - this.pos;
        int j = getInIfOpen().available();
        return i > Integer.MAX_VALUE - j ? Integer.MAX_VALUE : i + j;
    }

    public synchronized void mark(int paramInt) {
        this.marklimit = paramInt;
        this.markpos = this.pos;
    }

    public synchronized void reset() throws IOException {
        getBufIfOpen();
        if (this.markpos < 0) {
            throw new IOException("Resetting to invalid mark");
        }
        this.pos = this.markpos;
    }

    public boolean markSupported() {
        return true;
    }

    public void close() throws IOException {
        byte[] arrayOfByte;
        while ((arrayOfByte = this.buf) != null) {
            if (bufUpdater.compareAndSet(this, arrayOfByte, null)) {
                InputStream localInputStream = this.in;
                this.in = null;
                if (localInputStream != null) {
                    localInputStream.close();
                }
                return;
            }
        }
    }
}
分步骤分析源代码
不考虑mark,逻辑如下

这里写图片描述

  1. 当前有一个输入流为:1到17数字,有一个8字节的缓冲区数组
  2. 当应用程序开始读取数据的时候,如果pos=count(缓冲区没有数据,或者缓冲区数据已经读取完),需要重新重输入流中读出数据到缓冲数组,具体代码如下

  3. 读取流程如图所示,知道读取完所有数据

public synchronized int read() throws IOException {
        if (this.pos >= this.count) {
            fill();     //向缓冲区中填写数据
            if (this.pos >= this.count) {
                return -1;
            }
        }
        return getBufIfOpen()[(this.pos++)] & 0xFF;
    }
考虑mark,逻辑如下
情景1

这里写图片描述

情景2

这里写图片描述

情景3

这里写图片描述

情景4

这里写图片描述

情景5

这里写图片描述

对应代码翻译
    private void fill() throws IOException {
        byte[] localObject = getBufIfOpen();    //获取缓冲区数组
        if (this.markpos < 0) {         //初始值为-1,当markpos值为-1时,逻辑为未使用mark,
            this.pos = 0;
        } else if (this.pos >= localObject.length) {    //pos值等于缓冲区数组最大长度,缓冲区没有空间了
            if (this.markpos > 0) {     //【情景1】【情景2】markpos位置在缓冲区数组中间位置
                int i = this.pos - this.markpos;
                System.arraycopy(localObject, this.markpos, localObject, 0, i);     //标记区间前移,缓冲区空间为剩下的大小
                this.pos = i;
                this.markpos = 0;   //将markpos置为0,当下次缓冲区数组不够用时候,markpos将会失效
            } else if (localObject.length >= this.marklimit) {  
                //【情景3】
                //此时的markpos = 0,如果条件成立,localObject.length >= this.marklimit + markpos
                //根据文档定义,readlimit 参数告知此输入流在标记位置无效之前允许读取的字节数
                //条件成立,读取的长度已经超过了被允许读取的字节数,mark失效
                this.markpos = -1;
                this.pos = 0;
            } else {
                //此时的markpos = 0,且localObject.length < this.marklimit
                //readlimit标记的长度超过了缓冲区数组,扩充缓冲区数组大小
                //读取的长度未超过了被允许读取的字节数,mark有效,

                if (localObject.length >= MAX_BUFFER_SIZE) {
                    throw new OutOfMemoryError("Required array size too large");
                }
                int i = this.pos <= MAX_BUFFER_SIZE - this.pos ? this.pos * 2 : MAX_BUFFER_SIZE;
                //【情景4】扩展区间不超过标记后允许读取的字节
                if (i > this.marklimit) {
                    i = this.marklimit;
                }

                //不是情景4,即为【情景5】
                byte[] arrayOfByte = new byte[i];
                System.arraycopy(localObject, 0, arrayOfByte, 0, this.pos);
                if (!bufUpdater.compareAndSet(this, localObject, arrayOfByte)) {
                    throw new IOException("Stream closed");
                }
                localObject = arrayOfByte;
            }
        }
        this.count = this.pos;
        //从设备中读取数据到缓冲区数组中
        int i = getInIfOpen().read((byte[]) localObject, this.pos, localObject.length - this.pos);
        if (i > 0) {
            //获取缓冲区数组末位置
            this.count = (i + this.pos);
        }
    }
readlimit相关的问题

原文档
这里写图片描述

由情景3得知,markpos失效不仅需要超过readlimit,而且还需要达到缓冲区填满,才会失效

完整图

图太大了,提供一个链接:http://download.csdn.net/download/dengjili/10267728

运用一下刚刚的分析

读取两次输入数据流

具体代码如下

package other;

import java.io.ByteArrayInputStream;
import java.io.IOException;

public class MarkExample {

    public static void main(String[] args) {
        try {
            // 初始化一个字节数组,内有10个字节的数据
            byte[] bytes = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
            // 用一个ByteArrayInputStream来读取这个字节数组
            ByteArrayInputStream in = new ByteArrayInputStream(bytes);
            // 将ByteArrayInputStream包含在一个BufferedInputStream
            BufferedInputStream bis = new BufferedInputStream(in, 10);

            //超出缓冲区,情景4
            bis.mark(12);
            //bis.mark(11);报错,请自己分析一下,很容易的

            int c;
            while ( (c = bis.read()) != -1) {
                System.out.print(c + ",");
            }

            System.out.println("\nreset");
            bis.reset();
            while ( (c = bis.read()) != -1) {
                System.out.print(c + ",");
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

测试结果

1,2,3,4,5,6,7,8,9,10,11,
reset
1,2,3,4,5,6,7,8,9,10,11,
拓展

分析bis.mark(11);报错,请自己分析一下,很容易的

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值