Buffer 缓冲
为什么需要缓冲?
思考:没有buffer之前的读写。
子类
常见类型的缓冲
ByteBuffer
public abstract class ByteBufferextends Bufferimplements Comparable<ByteBuffer>
{}
ByteBuffer是抽象类无法直接实例化,可以通过allocate等方法返回实例。
allocate
public static ByteBuffer allocate(int capacity) {if (capacity < 0)throw new IllegalArgumentException();return new HeapByteBuffer(capacity, capacity);}
allocateDirect
public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);}
ByteBuffer 分为HeapByteBuffer和DirectByteBuffer两种,翻译成人话就是堆字节缓冲和直接字节缓冲,第二种不受 JVM 控制。
ByteBuffer hbb = ByteBuffer.allocate(1024);ByteBuffer dbb = ByteBuffer.allocateDirect(1024);
在后面的介绍中,hbb代指 JVM 缓冲区,dbb代指直接缓冲区。
HeapByteBuffer
DirectByteBuffer
常见方法
allocate
下面对 allocate 的调用链路参数进行分析。
public static ByteBuffer allocate(int capacity) {if (capacity < 0)throw new IllegalArgumentException();//capacity >= 0return new HeapByteBuffer(capacity, capacity);}
cap = lim = capacity,由 HeapByteBuffer 构造函数调用 super
HeapByteBuffer(int cap, int lim) { super(-1, 0, lim, cap, new byte[cap], 0);}
mark = -1
pos = 0
lim = cap
cap = cap
hb = new byte[cap]
offset = 0
ByteBuffer(int mark, int pos, int lim, int cap, byte[] hb, int offset){super(mark, pos, lim, cap); //-1,0,cap,capthis.hb = hb; //new byte[cap]this.offset = offset; //0}
hb字节数组和offset 偏移量为 ByteBuffer 字段。
mark = -1
pos = 0
lim = cap
cap = cap
Buffer(int mark, int pos, int lim, int cap) { //判断多余 cap > 0 if (cap < 0)throw new IllegalArgumentException("Negative capacity: " + cap); this.capacity = cap; // 1limit(lim); //2position(pos); //3 //汇总分析的参数 mark = -1,pos = 0if (mark >= 0) {if (mark > pos)throw new IllegalArgumentException("mark > position: ("+ mark + " > " + pos + ")");this.mark = mark;}}
核心代码表示为1,2,3的部分。
public final Buffer limit(int newLimit) { // newLimit = lim = capif ((newLimit > capacity) || (newLimit < 0))throw new IllegalArgumentException();limit = newLimit; //limit = newLimit = lim = capif (position > limit) position = limit; // position = 0 limit = capif (mark > limit) mark = -1; // mark = -1return this;}
public final Buffer position(int newPosition) { // newPosition = pos = pos = 0if ((newPosition > limit) || (newPosition < 0))throw new IllegalArgumentException();position = newPosition; // position = newPosition = pos = pos = 0if (mark > position) mark = -1;return this;}
所以经过这一串构造函数后,buffer中的值分别为
java.nio.HeapByteBuffer[pos=0 lim=1024 cap=1024]
pos = 位置,lim = 限制,cap = 容量,最大值。
allocateDirect
public static ByteBuffer allocateDirect(int capacity) {return new DirectByteBuffer(capacity);}
DirectByteBuffer(int cap) { //cap = capacity//调用链路super(-1, 0, cap, cap);//boolean pa = VM.isDirectMemoryPageAligned();int ps = Bits.pageSize();long size = Math.max(1L, (long)cap + (pa ? ps : 0));Bits.reserveMemory(size, cap);long base = 0;try {//unsafe 分配内存空间base = unsafe.allocateMemory(size);} catch (OutOfMemoryError x) {Bits.unreserveMemory(size, cap);throw x;}unsafe.setMemory(base, size, (byte) 0);if (pa && (base % ps != 0)) {// Round up to page boundaryaddress = base + ps - (base & (ps - 1));} else {address = base;}cleaner = Cleaner.create(this, new Deallocator(base, size, cap));att = null;}
MappedByteBuffer(int mark, int pos, int lim, int cap) { // package-privatesuper(mark, pos, lim, cap);this.fd = null;}
ByteBuffer(int mark, int pos, int lim, int cap) { // package-privatethis(mark, pos, lim, cap, null, 0);}
ByteBuffer(int mark, int pos, int lim, int cap, // package-privatebyte[] hb, int offset){super(mark, pos, lim, cap);this.hb = hb;this.offset = offset;}
Buffer(int mark, int pos, int lim, int cap) { // package-privateif (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;}}
调用过程不再赘述,跟 allocate 链路差不多,区别是使用 unsafe 进行内存分配和其他的操作。
flip 翻转
public final Buffer flip() {limit = position;position = 0;mark = -1;return this;}
案例
ByteBuffer hbb = ByteBuffer.allocate(1024);System.out.println(hbb.toString());hbb.flip();System.out.println(hbb.toString());
输出
java.nio.HeapByteBuffer[pos=0 lim=1024 cap=1024]
java.nio.HeapByteBuffer[pos=0 lim=0 cap=1024]
hasRemaining 是否有剩余
public final boolean hasRemaining() {return position < limit;}
position < limit 代表有剩余,position = limit 代表空间已经满了
clear 清除
public final Buffer clear() {position = 0;limit = capacity;mark = -1;return this;}
将buffer三个指针位置重置。
get
hbb
final byte[] hb; public byte get() {return hb[ix(nextGetIndex())];}
dbb
public byte get() {return ((unsafe.getByte(ix(nextGetIndex()))));}
asReadOnlyBuffer 只读buffer
public ByteBuffer asReadOnlyBuffer() {return new HeapByteBufferR(hb, this.markValue(), this.position(), this.limit(),this.capacity(), offset);}
buffer 案例1 基本操作读和写
创建5个容量的buffer,依次将i放进去,翻转buf,依次读出来。
public class byteBuffer02 {public static void main(String[] args) {ByteBuffer buffer = ByteBuffer.allocate(5);for (byte i = 0; i < buffer.capacity(); i++) {buffer.put(i);}buffer.flip();while (buffer.hasRemaining()) {System.out.println(buffer.get());}}
}
buffer 案例2 不同类型数据读写
public class ByteBuffer03 {public static void main(String[] args) {ByteBuffer bb = ByteBuffer.allocate(64);bb.putInt(100);bb.putLong(9);bb.putChar('a');bb.putShort((short) 4);bb.flip();System.out.println("----");System.out.println(bb.getInt());System.out.println(bb.getLong());System.out.println(bb.getChar());System.out.println(bb.getShort());}
}
buffer 案例3 只读 buffer
public class ByteBuffer04 {public static void main(String[] args) {ByteBuffer b1 = ByteBuffer.allocate(64);ByteBuffer b2 = b1.asReadOnlyBuffer();b1.putInt(5);b2.putInt(5);}
}
MappedByteBuffer
深入浅出java.nio.MappedByteBuffer
Channel 通道
实现
源码
public interface Channel extends Closeable {//是否开启public boolean isOpen();//关闭public void close() throws IOException;}
FileChannel
开启通道的几种方式
static File file = new File("D://a.txt");
从 RandomAccessFile 中getChannel
RandomAccessFile raf = new RandomAccessFile(file, "rw");FileChannel fc = raf.getChannel();
从 stream 中 getChannel
FileOutputStream fos = new FileOutputStream(file);FileChannel fc2 = fos.getChannel();
FileChannel.open
FileChannel fc3 = FileChannel.open(null, null);
案例1
将 uuid 读出来
public class ReadFileUid {static File file = new File("D://a.txt");public static void main(String[] args) throws Exception {ByteBuffer bf = ByteBuffer.allocate(1024);try (RandomAccessFile raf = new RandomAccessFile(file, "rw");FileChannel fc = raf.getChannel()) {int read = fc.read(bf);System.out.println(read);System.out.println(bf);bf.flip();System.out.println(bf);byte[] bytes = new byte[bf.limit()];bf.get(bytes);System.out.println(bf);System.out.println(Arrays.toString(bytes));String str = new String(bytes, StandardCharsets.UTF_8);System.out.println(str);}}
}
输出
36
java.nio.HeapByteBuffer[pos=36 lim=1024 cap=1024]
java.nio.HeapByteBuffer[pos=0 lim=36 cap=1024]
java.nio.HeapByteBuffer[pos=36 lim=36 cap=1024]
[54, 51, 51, 53, 98, 102, 53, 97, 45,
99, 57, 100, 54, 45, 52, 53, 51, 99,
45, 98, 57, 99, 98, 45, 102, 50, 52,
102, 56, 97, 54, 101, 98, 50, 99, 50]
6335bf5a-c9d6-453c-b9cb-f24f8a6eb2c2
案例2
将 uuid 写到文件中
public class WriteFileUid {static File file = new File("D://a.txt");public static void main(String[] args) throws Exception {ByteBuffer bf = ByteBuffer.allocate(1024);try (RandomAccessFile raf = new RandomAccessFile(file, "rw");FileChannel fc = raf.getChannel()) {String uuid = UUID.randomUUID().toString();System.out.println(uuid);byte[] bytes = uuid.getBytes(StandardCharsets.UTF_8);System.out.println(Arrays.toString(bytes));System.out.println(bf);bf.put(bytes);System.out.println(bf);bf.flip();System.out.println(bf);int write = fc.write(bf);System.out.println(write);}}
}
efdd8059-4f47-42e9-b9f9-aadd4a324309
[101, 102, 100, 100, 56, 48, 53, 57, 45, 52, 102, 52, 55,45, 52, 50, 101, 57, 45, 98, 57, 102, 57, 45, 97, 97,100, 100, 52, 97, 51, 50, 52, 51, 48, 57]
java.nio.HeapByteBuffer[pos=0 lim=1024 cap=1024]
java.nio.HeapByteBuffer[pos=36 lim=1024 cap=1024]
java.nio.HeapByteBuffer[pos=0 lim=36 cap=1024]
36