`
san_yun
  • 浏览: 2654420 次
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

BufferedInputStream 深入研究。

    博客分类:
  • java
 
阅读更多

1. BufferedInputStream的基本原理

BuffredInputStream存在的意义在于其提供了一个内部缓冲区,当read的的时候先一次性的把byte读取到内部的缓冲区,以后每次调用read(byte[])实际是从缓冲区 copy数据。缓冲区的大小可以通过BufferedInputStream(InputStream in, int size)构造函数设置,默认大小8192。

 

2.BufferedInputStream的实现

 

注意read方法是同步的:

  public synchronized int read(byte b[], int off, int len)
	throws IOException
    {
        getBufIfOpen(); // Check for closed stream
        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
	    throw new IndexOutOfBoundsException();
	} else if (len == 0) {
            return 0;
        }

	int n = 0;
        for (;;) {
            int nread = read1(b, off + n, len - n);
            if (nread <= 0) 
                return (n == 0) ? nread : n;
            n += nread;
            if (n >= len)
                return n;
            // if not closed but no bytes available, return
            InputStream input = in;
            if (input != null && input.available() <= 0)
                return n;
        }
    }

 实现都委托给里read1()

   private int read1(byte[] b, int off, int len) throws IOException {
	int avail = count - pos;
	if (avail <= 0) {
	    /* If the requested length is at least as large as the buffer, and
	       if there is no mark/reset activity, do not bother to copy the
	       bytes into the local buffer.  In this way buffered streams will
	       cascade harmlessly. */
	    if (len >= getBufIfOpen().length && markpos < 0) {
		return getInIfOpen().read(b, off, len);
	    }
	    fill();
	    avail = count - pos;
	    if (avail <= 0) return -1;
	}
	int cnt = (avail < len) ? avail : len;
	System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
	pos += cnt;
	return cnt;
    }

 fill:

/**
     * Fills the buffer with more data, taking into account
     * shuffling and other tricks for dealing with marks.
     * Assumes that it is being called by a synchronized method.
     * This method also assumes that all data has already been read in,
     * hence pos > count.
     */
    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;
    }

 3.BufferedInputStream对buffer的并发保护

 

buffreedInputStream是有状态的,需要考虑线程安全的问题,那么来看看它是如何保护buffer。首先

buf被定义为volatile,并且通过AtomicReferenceFieldUpdater来保护。

 

    /**
     * The internal buffer array where the data is stored. When necessary,
     * it may be replaced by another array of
     * a different size.
     */
    protected volatile byte buf[];

    /**
     * Atomic updater to provide compareAndSet for buf. This is
     * necessary because closes can be asynchronous. We use nullness
     * of buf[] as primary indicator that this stream is closed. (The
     * "in" field is also nulled out on close.)
     */
    private static final 
        AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = 
        AtomicReferenceFieldUpdater.newUpdater
        (BufferedInputStream.class,  byte[].class, "buf");
 AtomicReferenceFieldUpdater基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。用法:

 

fill的时候:
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");
}
close()的时候:
    public void close() throws IOException {
        byte[] buffer;
        while ( (buffer = buf) != null) {
            if (bufUpdater.compareAndSet(this, buffer, null)) {
                InputStream input = in;
                in = null;
                if (input != null)
                    input.close();
                return;
            }
            // Else retry in case a new buf was CASed in fill()
        }
    }
4.BufferedInputStream使用的注意点
 这段代码有一隐含的bug,也是使用inputStream读取容易出错的地方。
	String str = "hellohellohelloh123";
		ByteArrayInputStream input = new ByteArrayInputStream(str.getBytes());
		BufferedInputStream buffer = new BufferedInputStream(input);
		ByteArrayOutputStream out = new ByteArrayOutputStream(8192);
		byte[] chunk = new byte[3];
		int readByte = -1;

		while ((readByte = buffer.read(chunk)) != -1) {
			
			out.write(chunk);
		}

		System.out.println(str);
		System.out.println(out.toString());
 这段代码的输出结果:
hellohellohelloh123
hellohellohelloh12312
如果在while中加入System.out.println(readByte + ":" + new String(chunk));可以打印出:
3:hel
3:loh
3:ell
3:ohe
3:llo
3:h12
1:312
 最后一次read只读了1个byte,所以out.write(chunk)应该修改为
out.write(chunk, 0, readByte);
 
分享到:
评论

相关推荐

    JDK源码 src

    例如,深入研究`java.lang.Object`类,你会发现它是所有Java类的根,其`equals()`和`hashCode()`方法是实现对象比较和哈希表操作的关键。通过源码,我们可以学习到如何正确地重写这些方法以满足特定需求,从而避免...

    Android应用源码之VideoViewSample-IT计算机-毕业设计.zip

    VideoViewSample是一个专为毕业设计学习而准备的源码示例,通过深入研究这个项目,我们可以了解到如何在Android平台上实现视频播放功能,并进一步提升我们的App移动开发能力。 首先,VideoView是Android SDK提供的...

    JAVA文件压缩与解压缩实践(源代码+论文).zip

    Java文件压缩与解压缩是Java开发中常见的操作,特别是在数据传输、...在实践中,还可以进一步研究其他压缩算法,如LZ4、BZip2等,以及第三方库如Apache Commons Compress,它们提供了更丰富的压缩选项和更好的性能。

    设计模式之装饰模式

    装饰模式是一种结构型设计模式,它允许在运行时给对象添加新的行为或责任,而无需修改对象的源代码。...所以,无论是对于初学者还是有经验的开发者,"设计模式之装饰模式"都是一个值得深入研究和掌握的重要知识领域。

    JAVA文件压缩与解压缩实践(源代码+论文).rar

    Java文件压缩与解压缩是...以上内容详细介绍了Java文件压缩与解压缩的实践,包括使用ZIP和GZIP格式,以及如何在毕业设计和论文中进行深入研究。提供的"a.txt"文件可以作为实际操作的基础,进一步理解这些概念和技术。

    javaIO学习课件 很详细的讲解

    Java I/O(输入/输出)是Java编程语言中不可或缺的一部分,它允许程序与外部资源进行数据交换,包括读取文件、...通过深入研究这些材料,开发者可以更好地理解和利用Java的I/O系统,从而编写出更高效、更健壮的程序。

    JAVA局域网飞鸽传书软件设计与实现(源代码+LW).zip

    “源码”意味着我们有机会深入研究项目的实现细节,这包括但不限于:文件选择和上传的用户界面设计、错误处理机制、文件分块传输策略(如果文件过大)、安全性考虑(如防止非法访问或文件篡改)以及性能优化措施。...

    java应用工程

    Java应用工程是一个涵盖广泛主题的学习资源集合,主要集中在Java编程语言的应用上。这个压缩包包含了一系列的代码...通过深入研究和实践这些代码示例,你可以更好地掌握Java的各个方面,并在实际项目中应用这些知识。

    基于java的局域网飞鸽传书软件设计与实现(源代码+LW).zip

    【描述】中的“源码”指的是该项目提供了完整的程序源代码,这意味着我们可以深入研究和学习其背后的编程逻辑和技术实现。这对于初学者或者希望深入理解网络文件传输机制的开发者来说是非常有价值的资源。 【标签】...

    应用源码之IOStreamSample.zip

    《Android源码解析:深入理解IOStreamSample》 在Android应用开发中,对输入输出流(IOStream)的熟练掌握是至关重要的。...通过深入研究并动手实践,开发者将能更好地应对实际项目中的数据读写挑战。

    JAVA复习材料

    2. **集合框架**:深入研究ArrayList、LinkedList、HashMap、TreeMap等集合类的源码,了解其内部实现原理,比如扩容策略、元素存储方式等。 3. **并发编程**:分析Thread、synchronized、volatile、Lock等并发工具...

    疯狂Java flashget源码

    通过深入研究"疯狂Java flashget源码",不仅可以提升Java多线程编程的技巧,还可以学习到如何在实际项目中应用并发技术,解决网络下载中的各种问题。这是一个绝佳的学习平台,适合那些希望在并发编程和网络I/O方面...

    socket多线程,io流传输应用项目demo

    Socket编程是网络通信的核心技术,它允许两个网络应用程序通过TCP/IP协议进行数据交换。在这个"socket多线程,io流传...通过深入研究和理解这个项目,开发者可以提升自己的网络编程技能,更好地应对实际工作中的挑战。

    解析AndroidManifest源码

    在Android应用开发中,`AndroidManifest.xml`是一个至关重要的文件,它是每个Android应用程序的核心组成部分,...这个过程不仅对于Android系统理解和应用开发至关重要,也是深入研究Android系统底层机制的重要切入点。

    java io流学习笔记1

    对于源码级别的理解,你可以深入研究Java的`java.io`包,了解这些流的实现原理。例如,`BufferedInputStream`是如何通过内部缓冲区提高读取效率的,或者`ObjectInputStream`和`ObjectOutputStream`如何实现对象的...

    flv 播放器源码 全部

    《FLV播放器源码详解》 FLV(Flash Video)播放器源码是用于解析、解码并播放FLV格式视频文件的核心程序。...通过深入研究和实践,开发者可以更好地掌握网络流媒体播放技术,提升自身在该领域的专业能力。

    java源码:Java声音播放程序源代码.rar

    在Java编程语言中,开发一个声音播放程序是一个常见的任务,特别是在多媒体应用或者游戏开发中。...通过深入研究这个源代码,我们可以提升Java音频编程的能力,并可能为自己的项目带来更丰富的音频体验。

    structure.zip

    在IT行业中,设计模式是软件开发中的重要概念,它们代表了在特定场景下解决常见问题的最佳实践。结构型设计模式是其中一类...对于每个模式,深入研究其代码实现,分析其工作原理,将有助于提升你的编程技能和设计思维。

    java铁人下载系统.zip

    "Java铁人下载系统"是一个基于Java技术和JSP(JavaServer Pages)...通过深入研究这个项目,开发者不仅可以提升Java Web开发技能,还能了解文件下载服务的实现细节,对于个人的技术成长和项目经验积累具有积极意义。

Global site tag (gtag.js) - Google Analytics