1 概述
Java NIO 由以下几个核心部分组成:
- Buffer
- Channel
- Selectors
相关类的使用方法可以参考Java NIO 系列教程,写的通俗易懂。
本文主要从源码方面分析一下Buffer类
2 Buffer类
Buffer类是一个抽象类,所有的XxxBuffer均继承此类。Buffer类的作用跟BufferedReader和BufferedInputStream类中的缓冲区作用是一样的。只不过线程Buffer独立出来,而且添加了更多的功能。
2.1 构造函数
public abstract class Buffer {
private int mark = -1;
private int position = 0;
private int limit;
private int capacity;
// 在 direct buffers 中使用
long address;
Buffer(int mark, int pos, int lim, int cap) {
if (cap < 0)
throw new IllegalArgumentException("Negative capacity: " + cap);
this.capacity = cap;
limit(lim);
position(pos);
if (mark >= 0) {
if (mark > pos)
throw new IllegalArgumentException("mark > position: ("
+ mark + " > " + pos + ")");
this.mark = mark;
}
}
}
各属性变量的含义如下:
- mark:初始值为-1,用于备份当前的position
- position:初始值为0。position表示当前可以写入或读取数据的位置。当写入或读取一个数据后, position向前移动到下一个位置。
- limit:
- 写模式下,limit表示最多能往Buffer里写多少数据,等于capacity值。
- 读模式下,limit表示最多可以读取多少数据。
- capacity:缓存数组大小
2.2 主要方法
//返回容量
public final int capacity() {
return capacity;
}
// 返回当前位置
public final int position() {
return position;
}
//返回临界值
public final int limit() {
return limit;
}
//记录当前位置
public final Buffer mark() {
mark = position;
return this;
}
//恢复到之前记录位置
public final Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
position = m;
return this;
}
//清除缓冲区,即恢复变量的初始位置,但不清除缓冲区中的内容
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
//由写模式转为读模式
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
//重置position为0,从头读写数据或读数据
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
3 ByteBuffer类
Java NIO 有以下Buffer类型
- ByteBuffer
- CharBuffer
- DoubleBuffer
- FloatBuffer
- IntBuffer
- LongBuffer
- ShortBuffer
- MappedByteBuffer
其中MappedByteBuffer的实现比较特殊,其余各对应一种基础数据类型的缓冲区,实现原理是差不多的,下面以ByteBuffer为例介绍。
ByteBuffer也是一个抽象类,它的实现类有HeapByteBuffer和DirectByteBuffer两种。
- HeapByteBuffer:是在jvm虚拟机的堆上申请内存空间
- DirectByteBuffer:是直接在物理内存中申请内存空间
3.1 Buffer的分配
要想获得一个Buffer对象首先要进行分配。 每一个Buffer类都有一个allocate方法。
// 堆上面的分配
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity);
}
// 直接在物理内存上分配
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
3.2 ByteBuffer的读写
从ByteBuffer中读写数据有两种方式(其余Buffer类也一样):通过Channel类的read、write函数从Buffer中读写(后续Channel中介绍)
ByteBuffer的get、put函数
public abstract byte get();
public abstract byte get(int index);
public abstract ByteBuffer put(byte b);
public abstract ByteBuffer put(int index, byte b);
3.2 Compact函数
ByteBuffer类比基类Buffer多了Compact函数,compact()方法将所有未读的数据拷贝到Buffer起始处。然后将position设到最后一个未读元素正后面。limit属性依然像clear()方法一样,设置成capacity。现在Buffer准备好写数据了,但是不会覆盖未读的数据。
public abstract ByteBuffer compact();
3.3 HeapByteBuffer子类
HeapByteBuffer是ByteBuffer的实现子类,内部是通过byte数组实现在堆上的缓冲区,其余的get、put、compact函数均通过数组下标操作实现,比较简单,源码就不列了。
protected final byte[] hb;
HeapByteBuffer(int cap, int lim) {
super(-1, 0, lim, cap, new byte[cap], 0);
}
HeapByteBuffer(byte[] buf, int off, int len) {
super(-1, off, off + len, buf.length, buf, 0);
}
3.4 DirectByteBuffer子类
DirectByteBuffer是在jvm堆外直接申请一块空间,涉及Unsafe类, 其把文件映射到该内存空间中,与MappedByteBuffer较为类似,在特大文件的读写方面效率非常高。由于比较特殊,将在下一篇详细介绍。
(未完,待续)
参考:http://ifeve.com/java-nio-all/
原文链接:https://blog.csdn.net/linxdcn/article/details/72896616