InputStream mark()方法readlimit参数真实含义

来源:互联网 发布:javaweb数据库 编辑:程序博客网 时间:2024/05/22 19:27

public synchronized voidmark(int readlimit)

Sets a mark position in this stream. The parameter readlimit indicates how many bytes can be read before a mark is invalidated. Callingreset() will reposition the stream back to the marked position ifreadlimit has not been surpassed. The underlying buffer may be increased in size to allowreadlimit number of bytes to be supported.
Parameters
readlimit the number of bytes that can be read before the mark is invalidated.

在流当中设置一个标记,readlimit参数指示在标记变为无效前,你最多可以读多少个字节。如果在设置标记后,你接着读取的字节数没有超过readlimit参数,那么你就可以通过调用reset方法将流指针复位到标记处,如果readlimit参数比较大,底层buf也会自动扩大其size以支持更大readlimit参数.

也就是说,当你调用mark(readlimit)方法在流当中设置一个标记后,你接下来最多只能读readlimit个字节,否则标记就可能失效。标记失效意味着你调用reset方法后会产生一个异常。


但实际的运行情况却和JAVA文档中的描述并不完全相符。很多情况下在BufferedInputStream类中调用mark(int readlimit)方法后,即使读取超过readlimit字节的数据,mark标记仍有效。这是为什么呢?只能从java源码来找到答案了。

类BufferedInputeStream中的fill()方法,是当缓存buf中的字节被读完后就会被调用,以便从底层流中载入新的数据到缓存buf中来。

 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 {            /* grow buffer */                int nsz = pos * 2;                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;    }

载入新数据到缓存buf的过程是这样的:
        if (markpos < 0)            pos = 0;            /* no mark: throw away the buffer */

如果流没有设置mark标记,很简单,将pos=0,直接丢弃缓存buf中的所有内容。重新读入buf.size大小的数据。

            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;            }
如果mark指针大于0,则丢弃缓存buf中位于mark指针之前的数据,且将mark指针之后的数据移动到缓存buf的0索引处来。这样mark指针变为0,缓存buf中剩余的空间就是丢弃掉的原来位于mark指针之前的那部分,然后在读入数据充满缓存buf.如果此时用户继续调用输入流的read()方法。当缓存buf中的字节再一次被读完后,该fill方法就会被再次调用,此时会发生两种情况:

第一种情况是缓存buf,size() 大于用户调用mark(readlimit)方法时所设置的readlimit参数:

           } else if (buffer.length >= marklimit) {                markpos = -1;   /* buffer got too big, invalidate mark */                pos = 0;        /* drop buffer contents */            }
当执行到此处时,说明用户在=设置mark标记后,接着又读取了刚好等于缓存buf.size大小的字节。这种情况,直接将markpos置为-1,即清除mark标记,直接载入buf.size大小的新数据。

第二中情况是buf,size() 小于于用户调用mark(readlimit)方法时所设置的readlimit参数:

else {            /* grow buffer */                int nsz = pos * 2;                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;            }


会直接将buf的大小增大为readlimit大小。此种情况,刚好正是JAVA文档中所说的,当用户调用mari(readlimit)方法设置标记后,如果接着又读取了超过readlimit个字节后,mark标记就会失效。

现在就可以总结bufferdinputStream mark(readlimit)方法的readlimit参数:

当用户调用mark(readlimit)方法设置标记后,如果readlimit小于缓存buf的大小,则只有读取超过buf.size大小字节后,mark标记才会失效;如果readlimit大于缓存buf的大小,则只有读取超过readlimit大小字节后,mark标记才会失效. 也就是说只有读取的字节数 > Math.max(readlimit,buf,size)后,mark标记才会失效。当然这只是BufferedInputSteam类的算法。无法确保其它输入流也是如此算法。所以为确保mark标记的有效,最好将读取的字节数限定在readlimit以内




原创粉丝点击