BufferedInputStream学习笔记

【本文转载于http://icanfly.iteye.com/blog/1207397】


BufferedInputStream是一个带有缓冲区域的InputStream,它的继承体系如下: 

InputStream 
|__FilterInputStream 
        |__BufferedInputStream
 

首先了解一下FilterInputStream: 
FilterInputStream通过装饰器模式将InputStream封装至内部的一个成员变量: 
Java代码  收藏代码
  1. protected volatile InputStream in;  

需要注意的是该成员变量使用了volatile关键字进行修饰,这意味着该成员变量的引用的内存可见性为多线程即时可见的。 
其它地方FilterInputStream将所有的操作委托给了in这个成员进行操作。 

了解了这些过后,来仔细看看BufferedInputStream的成员变量: 
Java代码  收藏代码
  1. private static int defaultBufferSize = 8192 //该变量定义了默认的缓冲大小  
  2.   
  3. protected volatile byte buf[]; //缓冲数组,注意该成员变量同样使用了volatile关键字进行修饰,作用为在多线程环境中,当对该变量引用进行修改时保证了内存的可见性。  
  4.   
  5. private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = AtomicReferenceFieldUpdater.newUpdater(BufferedInputStream.class,  byte[].class"buf")//缓存数组的原子更新器,该成员变量与buf数组的volatile关键字共同组成了buf数组的原子更新功能实现。  
  6.   
  7. protected int count;//该成员变量表示目前缓冲区域中有多少有效的字节。  
  8.   
  9. protected int pos;//该成员变量表示了当前缓冲区的读取位置。  
  10.   
  11. protected int markpos = -1;/*表示标记位置,该标记位置的作用为:实现流的标记特性,即流的某个位置可以被设置为标记,允许通过设置reset(),将流的读取位置进行重置到该标记位置,但是InputStream注释上明确表示,该流不会无限的保证标记长度可以无限延长,即markpos=15,pos=139734,该保留区间可能已经超过了保留的极限(如下)*/  
  12.   
  13. protected int marklimit;/*该成员变量表示了上面提到的标记最大保留区间大小,当pos-markpos> marklimit时,mark标记可能会被清除(根据实现确定)。*/  



通过构造函数可以看到:初始化了一个byte数组作为缓冲区域 
Java代码  收藏代码
  1. public BufferedInputStream(InputStream in, int size) {  
  2.     super(in);  
  3.         if (size <= 0) {  
  4.             throw new IllegalArgumentException("Buffer size <= 0");  
  5.         }  
  6.     buf = new byte[size];  
  7. }  



这个类中最为重要的方法是fill()方法,它提供了缓冲区域的读取、写入、区域元素的移动更新等。下面着重分析一下该方法: 
Java代码  收藏代码
  1. private void fill() throws IOException {  
  2.         byte[] buffer = getBufIfOpen();  
  3.     if (markpos < 0) {  
  4.           /*如果不存在标记位置(即没有需要进行reset的位置需求) 
  5.             则可以进行大胆地直接重置pos标识下一可读取位置,但是这样 
  6.             不是会读取到以前的旧数据吗?不用担心,在后面的代码里会实现输入流的新  
  7.             数据填充*/  
  8.         pos = 0;          
  9.     }else if (pos >= buffer.length){  
  10.        /* 位置大于缓冲区长度,这里表示已经没有可用空间了 */  
  11.         if (markpos > 0) {     
  12.              /* 表示存在mark位置,则要对mark位置到pos位置的数据予以保留, 
  13.                 以确保后面如果调用reset()重新从mark位置读取会取得成功*/  
  14.         int sz = pos - markpos;  
  15.                 /*该实现是通过将缓冲区域中markpos至pos部分的移至缓冲区头部实现*/  
  16.         System.arraycopy(buffer, markpos, buffer, 0, sz);  
  17.         pos = sz;  
  18.         markpos = 0;  
  19.         } else if (buffer.length >= marklimit) {  
  20.                 /* 如果缓冲区已经足够大,可以容纳marklimit,则直接重置*/  
  21.                 markpos = -1;     
  22.         pos = 0;/* 丢弃所有的缓冲区内容 */  
  23.         } else {          
  24.                 /* 如果缓冲区还能增长的空间,则进行缓冲区扩容*/  
  25.         int nsz = pos * 2;  
  26.                 /*新的缓冲区大小设置成满足最大标记极限即可*/  
  27.         if (nsz > marklimit)  
  28.             nsz = marklimit;  
  29.         byte nbuf[] = new byte[nsz];  
  30.                 //将原来的较小的缓冲内容COPY至增容的新缓冲区中  
  31.         System.arraycopy(buffer, 0, nbuf, 0, pos);  
  32.                 //这里使用了原子变量引用更新,确保多线程环境下内存的可见性  
  33.                 if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {  
  34.                     // Can't replace buf if there was an async close.  
  35.                     // Note: This would need to be changed if fill()  
  36.                     // is ever made accessible to multiple threads.  
  37.                     // But for now, the only way CAS can fail is via close.  
  38.                     // assert buf == null;  
  39.                     throw new IOException("Stream closed");  
  40.                 }  
  41.                 buffer = nbuf;  
  42.         }  
  43.         count = pos;  
  44.         //从原始输入流中读取数据,填充缓冲区  
  45.     int n = getInIfOpen().read(buffer, pos, buffer.length - pos);  
  46.         //根据实际读取的字节数更新缓冲区中可用字节数  
  47.         if (n > 0)  
  48.             count = n + pos;  
  49.     }  

整个fill的过程,可以看作是BufferedInputStream对外提供滑动读取的功能实现,通过预先读入一整段原始输入流数据至缓冲区中,而外界对BufferedInputStream的读取操作实际上是在缓冲区上进行,如果读取的数据超过了缓冲区的范围,那么BufferedInputStream负责重新从原始输入流中载入下一截数据填充缓冲区,然后外界继续通过缓冲区进行数据读取。这样的设计的好处是:避免了大量的磁盘IO,因为原始的InputStream类实现的read是即时读取的,即每一次读取都会是一次磁盘IO操作(哪怕只读取了1个字节的数据),可想而知,如果数据量巨大,这样的磁盘消耗非常可怕。而通过缓冲区的实现,读取可以读取缓冲区中的内容,当读取超过缓冲区的内容后再进行一次磁盘IO,载入一段数据填充缓冲,那么下一次读取一般情况下就直接可以从缓冲区读取,减少了磁盘IO。减少的磁盘IO大致可以通过以下方式计算(限read()方式): 

length  流的最终大小 
bufSize 缓冲区大小 

则通过缓冲区实现的输入流BufferedInputStream的磁盘IO数为原始InputStream磁盘IO的 
1/(length/bufSize) 

read方法解析:该方法返回当前位置的后一位置byte值(int表示). 
Java代码  收藏代码
  1. public synchronized int read() throws IOException {  
  2.     if (pos >= count) {  
  3.            /*表示读取位置已经超过了缓冲区可用范围,则对缓冲区进行重新填充*/  
  4.         fill();  
  5.            /*当填充后再次读取时发现没有数据可读,证明读到了流末尾*/  
  6.         if (pos >= count)  
  7.         return -1;  
  8.     }  
  9.         /*这里表示读取位置尚未超过缓冲区有效范围,直接返回缓冲区内容*/  
  10.     return getBufIfOpen()[pos++] & 0xff;  
  11. }  


一次读取多个字节(尽量读,非贪婪) 
Java代码  收藏代码
  1. private int read1(byte[] b, int off, int len) throws IOException {  
  2.     int avail = count - pos;  
  3.     if (avail <= 0) {  
  4.         /*这里使用了一个巧妙的机制,如果读取的长度大于缓冲区的长度 
  5.               并且没有markpos,则直接从原始输入流中进行读取,从而避免无谓的 
  6.               COPY(从原始输入流至缓冲区,读取缓冲区全部数据,清空缓冲区, 
  7.               重新填入原始输入流数据)*/  
  8.         if (len >= getBufIfOpen().length && markpos < 0) {  
  9.         return getInIfOpen().read(b, off, len);  
  10.         }  
  11.             /*当无数据可读时,从原始流中载入数据到缓冲区中*/  
  12.         fill();  
  13.         avail = count - pos;  
  14.         if (avail <= 0return -1;  
  15.     }  
  16.     int cnt = (avail < len) ? avail : len;  
  17.         /*从缓冲区中读取数据,返回实际读取到的大小*/  
  18.     System.arraycopy(getBufIfOpen(), pos, b, off, cnt);  
  19.     pos += cnt;  
  20.     return cnt;  
  21.     }  


以下方法和上面的方法类似,唯一不同的是,上面的方法是尽量读,读到多少是多少,而下面的方法是贪婪的读,没有读到足够多的数据(len)就不会返回,除非读到了流的末尾。该方法通过不断循环地调用上面read1方法实现贪婪读取。 
Java代码  收藏代码
  1. public synchronized int read(byte b[], int off, int len)  
  2.     throws IOException  
  3.     {  
  4.         getBufIfOpen(); // Check for closed stream  
  5.         if ((off | len | (off + len) | (b.length - (off + len))) < 0) {  
  6.         throw new IndexOutOfBoundsException();  
  7.     } else if (len == 0) {  
  8.             return 0;  
  9.         }  
  10.   
  11.     int n = 0;  
  12.         for (;;) {  
  13.             int nread = read1(b, off + n, len - n);  
  14.             if (nread <= 0)   
  15.                 return (n == 0) ? nread : n;  
  16.             n += nread;  
  17.             if (n >= len)  
  18.                 return n;  
  19.             // if not closed but no bytes available, return  
  20.             InputStream input = in;  
  21.             if (input != null && input.available() <= 0)  
  22.                 return n;  
  23.         }  
  24.     }  

略过多少字节 
Java代码  收藏代码
  1. public synchronized long skip(long n) throws IOException {  
  2.         getBufIfOpen(); // Check for closed stream  
  3.     if (n <= 0) {  
  4.         return 0;  
  5.     }  
  6.     long avail = count - pos;  
  7.        
  8.         if (avail <= 0) {  
  9.             // If no mark position set then don't keep in buffer  
  10.             //从上面的注释可以知道,这也是一个巧妙的方法,如果没有mark标记,  
  11.             // 则直接从原始输入流中skip  
  12.             if (markpos <0)   
  13.                 return getInIfOpen().skip(n);  
  14.               
  15.             // Fill in buffer to save bytes for reset  
  16.             fill();  
  17.             avail = count - pos;  
  18.             if (avail <= 0)  
  19.                 return 0;  
  20.         }  
  21.         //该方法的实现为尽量原则,不保证一定略过规定的字节数。  
  22.         long skipped = (avail < n) ? avail : n;  
  23.         pos += skipped;  
  24.         return skipped;  
  25.     }  

估计目前可用的字节数,原始流中可用的字节数+缓冲区中可用的字节数 
Java代码  收藏代码
  1. public synchronized int available() throws IOException {  
  2.     return getInIfOpen().available() + (count - pos);  
  3.     }  

标记位置: 
Java代码  收藏代码
  1. public synchronized void mark(int readlimit) {  
  2.     marklimit = readlimit;  
  3.     markpos = pos;  
  4.     }  

重置位置:该实现清晰的表明下一读取位置被推到了以前的标记位置,以实现重新读取区段的功能 
Java代码  收藏代码
  1. public synchronized void reset() throws IOException {  
  2.         getBufIfOpen(); // Cause exception if closed  
  3.     if (markpos < 0)  
  4.         throw new IOException("Resetting to invalid mark");  
  5.     pos = markpos;  
  6.     }  


关闭流:首先通过线程安全的方式设置了内部的缓冲区引用为空,然后再对原始输入流进行关闭。 
Java代码  收藏代码
  1. public void close() throws IOException {  
  2.         byte[] buffer;  
  3.         while ( (buffer = buf) != null) {  
  4.             if (bufUpdater.compareAndSet(this, buffer, null)) {  
  5.                 InputStream input = in;  
  6.                 in = null;  
  7.                 if (input != null)  
  8.                     input.close();  
  9.                 return;  
  10.             }  
  11.             // Else retry in case a new buf was CASed in fill()  
  12.         }  
  13.     }  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/262905.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文件上传案例——客户端和服务端套接字

一、文件上传原理 文件上传下载就是反复的输入流和输出流的read和wirte方法&#xff08;反复的内存和硬盘的交互&#xff09;&#xff1b; 二、实现 1、客户端实现&#xff1a; 2、服务端实现&#xff1a; 3、解决客户端和服务端两个程序在完成上传下载之后没有停止 原因是whil…

大家一起来博皮——2:液态布局和固态布局,页面框架篇

大家一起来博皮虽然博客园的皮肤很多&#xff0c;而且很漂亮。但是那些自己想更“个性化”自己博客皮肤的朋友&#xff0c;对博客园的皮肤模板还是颇多不满&#xff0c;认为皮肤的结构过于混乱&#xff0c;css样式难以掌控。针对这种情况&#xff0c;博客园开发团队在2007年底&…

Linux配置 DNS and BIND服务配置详解--缓存服务器配置 正反向解析配置

一、DNS简介一、DNS简介 DNS是计算机域名系统 (Domain Name System 或Domain Name Service) 的缩写&#xff0c;它是由域名解析器和域名服务器组成的。域名服务器是指保存有该网络中所有主机的域名和对应IP地址&#xff0c;并具有将域名转换为IP地址功能的服务器。其中域名必…

我的博客网站开发6——博文关键字搜索

在页面中&#xff0c;用户可以通过关键字的搜索功能搜索博文。可以实现类似百度和Google的页面搜索功能&#xff0c;可实现多个关键字的搜索。搜索后&#xff0c;在搜索的结果中有关键字的高亮度的提示如&#xff1a; 在搜索的结果页面&#xff0c;模仿Google的搜索页面的快照功…

shell 函数定义和调用

为什么80%的码农都做不了架构师&#xff1f;>>> 一. 函数定义 语法&#xff1a; [function] functionname[()]{action;[return int;] } 说明&#xff1a; 1、可以带function fun() 定义&#xff0c;也可以直接fun() 定义,不带任何参数。 2、参数返回&#xff0c;可…

Nhibernate代码生成器v2.1中文版

Nhibernate代码生成器v2.1中文版(转发)下载转载于:https://www.cnblogs.com/hakuci/archive/2008/03/15/1106802.html

Head First设计模式读书笔记——策略模式

问题描述&#xff1a; 目前的任务是实现一个FPS类游戏的各种角色&#xff08;友军、敌军、平民和狗、猫、鸭子等动物&#xff09;以及他们的各种行为&#xff08;攻击、游泳等&#xff09;。 设计方案一 很简单&#xff0c;只要实现一个角色超类&#xff0c;将角色的各种行为放…

centos+bond+bridge+docker(ssh容器)固定ip实现测试环境(一)

硬件&#xff1a;R730交换机&#xff1a;H3C Switch S5120-28P-SI系统&#xff1a;centos7#nmtuihttp://568273240.blog.51cto.com/802.3ad为LACP模式交换机部分&#xff1a;# systemctl restart network可以多重启几遍试下。http://568273240.blog.51cto.com/注意&#xff1a;…

简单线性回归算法

为什么80%的码农都做不了架构师&#xff1f;>>> /*** 简单线性回归算法* param array y轴数据* param array x轴数据* returns array(slope,intercept,r2)*/ function linearRegression(y, x) {var lr {};var n y.length;var sum_x 0;var sum_y 0;var sum_xy …

模拟BS服务器

一、模拟BS服务器分析 二、BS模拟服务器代码实现 图片都是单独请求&#xff0c;后台单独线程&#xff0c;这边是通过构造方法传入的Runable接口的实现类匿名对象创建线程&#xff1b; 创建本地输入流读取到网络输出流传过来的信息再放到网络输出流中返回&#xff1b; 转载于:ht…

不要62

题目 试题描述杭州人称那些傻乎乎粘嗒嗒的人为 62&#xff08;音&#xff1a;laoer&#xff09;。杭州交通管理局经常会扩充一些的士车牌照&#xff0c;新近出来一个好消息&#xff0c;以后上牌照&#xff0c;不再含有不吉利的数字了&#xff0c;这样一来&#xff0c;就可以消除…

CentOS 7 下的 Firewall

CentOS 7 默认实用的用Firewalld作为防火墙&#xff0c;摒弃了原先的iptables。但是内核还是使用iptable作为管理参考文档https://access.redhat.com/documentation/zh-CN/Red_Hat_Enterprise_Linux/7/html/Security_Guide/sec-Using_Firewalls.htmlhttp://www.myhome.net.tw/2…

POJ 1091(数论)

题目大意是给定两个整数n和m&#xff0c;求出长度为n1满足条件的数列data的个数&#xff0c;数列的要求下&#xff1a;1&#xff09;1<data[i]<m,for1<i<n2)data[n1]m;3&#xff09;这个n1个数满足&#xff1a;存在x1,x2,...,xn,xn1,满足x1*data[1]x2*data[2]...x(…

没有动任何配置文件,今天就出现了修改的JSP内容在页面不体现。依然是老的页面内容...

2019独角兽企业重金招聘Python工程师标准>>> 现象&#xff1a; 没有动任何配置文件&#xff0c;今天就出现了修改的JSP内容在页面不体现。依然是老的页面内容 问题分析: 既然可以用过&#xff0c;所以系统应该是检测文件更新的&#xff0c;之所以不重新编译JSP&…

PWA

转载于:https://www.cnblogs.com/QianDingwei/p/11359160.html

Windows XP SP3?转自作者: 孤单的鸽子,  出处:天极yesky, 责任编辑: 袁绍龙

经过测试和检查之后&#xff0c;我们认为&#xff0c;SP3并未为Windows XP系统引入任何新的功能&#xff0c;它仅仅是一款安全升级和BUG修正补丁程序集……原文&#xff1a;http://winsystem.ctocio.com.cn/windowsxp/321/7492821.shtml目前&#xff0c;互联网上围绕Windows XP…

创建表头固定,表体可滚动的GridView(转)

本方法只需要设置一个GridView的宽度&#xff0c;其它宽度不需要设置。测试环境&#xff1a;IE6&#xff0c;Firefox通过。 C# <% Page Language"C#" AutoEventWireup"true" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional/…

3G与4G到底有何区别?

对每一个手机用户来说&#xff0c;3G和4G都是手机术语词典中最神秘的词汇&#xff0c;然而在任何卖手机和平板电脑的地方你都能发现它们的身影。当你想买一部手机的时候&#xff0c;通常不清楚该选择哪一种&#xff0c;但有一点要明确——不要随便追求高的数字。这篇文章将帮助…

海尔5D净水洗热水器引领中国制造未来

在过去的一段时间里&#xff0c;智能家居与物联网相继成为热潮&#xff0c;云计算与大数据开始被应用于不同领域的情况下&#xff0c;越来越多的智能家居产品也开始走入普通消费者生活之中……在这些独特的产业创新风起云涌的情况下&#xff0c;越来越多的行业开始推动着中国制…

Lambda表达式——注重过程的编程思想

一、使用匿名内部类的匿名对象创建线程和Lambda表达式写法 Lambda表达式写法不用去定义一个Runable接口的实现类&#xff1b; 二、方法入参是一个接口或者接口的实现类 三、对某个类的一些对象实例进行排序 其中&#xff0c;数组创建时候使用的是静态初始化&#xff0c;里面存放…