承接上面一个主题: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到17数字,有一个8字节的缓冲区数组
当应用程序开始读取数据的时候,如果pos=count(缓冲区没有数据,或者缓冲区数据已经读取完),需要重新重输入流中读出数据到缓冲数组,具体代码如下
读取流程如图所示,知道读取完所有数据
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);报错,请自己分析一下,很容易的