Java 7之传统I/O第3篇 - BufferedInputStream和BufferedOutputStream类
来源:互联网 发布:蓝牙软件免费下载 编辑:程序博客网 时间:2024/05/16 02:01
无论是输入输出的字节流还是字符流,都广泛使用了一种设计模式 - 装饰器模式。其实Java I/O 库中的所有输入流、输出流的类都采用了装饰器模式,它们可以无限次地进行装饰转换,转换的目的就是得到自己想要的数据类型的流对象。
先来看一下带缓冲区输出流的构造函数,如下:
public BufferedInputStream(InputStream in) { this(in, defaultBufferSize); } public BufferedInputStream(InputStream in, int size) { super(in); if (size <= 0) { throw new IllegalArgumentException("Buffer size <= 0"); } buf = new byte[size]; }由于父类的持有的输入流引用是protected的,所以也就通过构造函数输入的输入流得到了引用,同时初始化缓冲区的大小,可以指定,也可以保持默认,默认为8192。
为了节约资源,有时候需要关闭输入流并释放缓冲区,这时候就可以调用close()方法,如下:
private static final AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater = AtomicReferenceFieldUpdater.newUpdater(BufferedInputStream.class, byte[].class, "buf");
public void close() throws IOException { byte[] buffer; while ( (buffer = buf) != null) { if (bufUpdater.compareAndSet(this, buffer, null)) {// 会将缓冲区buf的数据清空并释放缓冲区,也就是buf=null InputStream input = in; in = null; // 释放连接的资源 if (input != null) // 关闭输入流 input.close(); return; } // Else retry in case(防止) a new buf was CASed in fill() } }
这里有一个方法非常重要compareAndSet()方法。由于close()方法是异步的,所以就可能在调用close()方法时调用其他的方法,为了阻止这样的情况产生,每次都要对this中的buf和buffer进行比较,如果没有改动时执行缓冲区清空和输入流关闭操作,否则不做任何操作。
来看read()方法,源代码如下:
public synchronized int read() throws IOException { if (pos >= count) { fill(); if (pos >= count) return -1; } return getBufIfOpen()[pos++] & 0xff; }
来看最重要的read()方法,首先需要看一下fill()方法,源代码如下:
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; }
假如有两个线程,一个线程执行read方法(此时刚执行完 System.arraycopy(buffer, 0, nbuf, 0, pos)), 这时候系统调度到另一个线程执行close方法(close方法不是线程同步的)把buffer置为null,然后系统又调度到刚开始那个线程继续执行read方法,这时候执行buffer=nbuf, 好吧,这里就出现内存泄露了(close线程以为它释放了资源,其实不然),当我们加上这段代码:
if (!bufUpdater.compareAndSet(this, buffer, nbuf)) { throw new IOException(“Stream closed”); }在执行buffer=nbuf前检查buffer是修改过,就可以保证不会出现上述那种情况。
再来看一下有参数的read()方法,源码如下:
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; } 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; } }
BufferedOutputStream类是输出流,其中最主要的就是write()方法了,源代码如下:
// Writes the specified byte to this buffered output stream. public synchronized void write(int b) throws IOException { if (count >= buf.length) { flushBuffer(); } buf[count++] = (byte)b; } /** * Writes len bytes from the specified byte array * starting at offset off to this buffered output stream. */ public synchronized void write(byte b[], int off, int len) throws IOException { if (len >= buf.length) { /* If the request length exceeds the size of the output buffer, flush the output buffer and then write the data directly. In this way buffered streams will cascade harmlessly. */ flushBuffer(); out.write(b, off, len); return; } if (len > buf.length - count) { flushBuffer(); } System.arraycopy(b, off, buf, count, len); count += len; }两个方法非常简单,如果byte缓冲区的字符满了就写入输出流并且刷新缓存,但是应该考虑到,如果在最后一次写入缓冲区时,不论缓冲区是否满了,都要写入并且刷新缓冲区,这时候就要调用flush()方法了,如下:
private void flushBuffer() throws IOException { if (count > 0) { out.write(buf, 0, count); count = 0; } } public synchronized void flush() throws IOException { flushBuffer();// 写入缓冲区数据并刷 out.flush(); // 关闭输出流 }
0 0
- Java 7之传统I/O第3篇 - BufferedInputStream和BufferedOutputStream类
- Java I/O流-BufferedInputStream、BufferedOutputStream
- java BufferedOutputStream和BufferedInputStream
- Java BufferedInputStream 和 BufferedOutputStream
- Java 7之传统I/O第2篇 - InputStream和OutputStream
- Java 7之异步I/O第1篇 - 传统文件 I/O操作
- BufferedInputStream和BufferedOutputStream类
- BufferedInputStream和BufferedOutputStream类
- Java8 I/O源码-BufferedInputStream与BufferedOutputStream
- JAVA IO之BufferedInputStream&BufferedOutputStream
- Java IO BufferedInputStream和BufferedOutputStream
- Java 7之传统I/O - InputStreamReader和OutputStreamWriter
- Java 7之传统I/O - PipedInputStream和PipedOutputStream
- Java 7之传统I/O - ByteArrayInputStream和ByteArrayOutputStream
- Java 7之传统I/O - InputStreamReader和OutputStreamWriter
- Java 7之传统I/O第1篇- 输入输出流基础框架
- java基础之IO流中BufferedInputStream和BufferedOutputStream
- java核心技术之IO流(四)BufferedInputStream和BufferedOutputStream
- JRainbow开发进度
- Android创建和删除桌面快捷方式
- 精选WPHTML+CSS代码举例
- Android中线程通讯类Handler
- sql 语法大全2
- Java 7之传统I/O第3篇 - BufferedInputStream和BufferedOutputStream类
- Matlab功率谱估计
- eclipse @override 报错解决
- [Android]自定义组件示例:使用attrs.xml文件定制RadioButton
- MapReduce工作原理图文详解
- Linux下查看历史操作记录
- js 技巧
- 解决firefox频繁询问代理服务器用户名和密码问题
- hibernate one to many