前言
上篇
【從入門到放棄-Java】并發程式設計-NIO-Channel中我們學習到channel是雙向通道,資料通過channel在實體(檔案、socket)和緩沖區(buffer)中可以雙向傳輸。
本文我們就來學習下buffer
簡介
buffer即緩沖區,實際上是一塊記憶體,可以用來寫入、讀取資料。是一個線性的、大小有限的、順序承載基礎資料類型的記憶體塊。
buffer有三個重要的屬性:
- capacity:緩沖池大小,是不可變的。當buffer寫滿時,需要先清空才能繼續寫入。
- limit:是buffer中不可以被讀或者寫的第一個元素的位置,limit的大小永遠不會超過capacity(在寫模式下,limit等于capacity)
- position:是buffer中可以被讀或者寫的第一個元素的位置,position的大小永遠不會超過limit
除了boolean外,每一個基礎資料類型都有對應的buffer。如:ByteBuffer、CharBuffer、LongBuffer等
buffer不是線程安全的,如果要在多線程中使用 需要加鎖控制
接下來以ByteBuffer為例開始學習。
ByteBuffer
allocateDirect
public static ByteBuffer allocateDirect(int capacity) {
//會建立一個容量大小為capacity的DirectByteBuffer(ByteBuffer的子類)
return new DirectByteBuffer(capacity);
}
allocate
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw createCapacityException(capacity);
//會建立一個容量大小為capacity的HeapByteBuffer(ByteBuffer的子類)
return new HeapByteBuffer(capacity, capacity);
}
HeapByteBuffer和DirectByteBuffer的差別:
- DirectByteBuffer是直接調用native方法在本機os::malloc()建立堆外記憶體;HeapByteBuffer是直接在jvm的堆中配置設定記憶體。
- 當buffer中的資料和磁盤、網絡等的互動都在作業系統的核心中發生時,使用DirectByteBuffer能避免從核心态->使用者态->核心态的切換開銷,所有的處理都在核心中進行,性能會比較好
- 當頻繁建立操作資料量比較小的buffer時,使用HeapByteBuffer在jvm堆中配置設定記憶體能抵消掉使用DirectByteBuffer帶來的好處。
wrap
public static ByteBuffer wrap(byte[] array,
int offset, int length)
{
try {
return new HeapByteBuffer(array, offset, length);
} catch (IllegalArgumentException x) {
throw new IndexOutOfBoundsException();
}
}
public static ByteBuffer wrap(byte[] array) {
return wrap(array, 0, array.length);
}
将byte數組包裝成一個ByteBuffer
讀資料
- 使用get方法從Buffer中讀取資料
- 從Buffer中讀取資料到Channel即:Channel::write() (從buffer中讀取資料寫入到資源中,是以是write)
寫資料
- 使用put方法直接設定Buffer中的資料
- 從Channel中讀取資料到Buffer即:Channel::read() (從資源中讀取資料寫入到buffer中,是以是read)
position
//擷取buffer中目前position的位置
public final int position() {
return position;
}
//設定buffer的position為newPosition,注意newPosition要大于0且小于limit,如果remark大于newPosition則設定為-1
public Buffer position(int newPosition) {
if (newPosition > limit | newPosition < 0)
throw createPositionException(newPosition);
position = newPosition;
if (mark > position) mark = -1;
return this;
}
limit
//擷取buffer中目前limit的位置
public final int limit() {
return limit;
}
//設定buffer的limit為newLimit,注意newLimit要大于0且小于capacity。如果position大于newLimit這設定為newLimit,如果remark大于newLimit則設定為-1
public Buffer limit(int newLimit) {
if (newLimit > capacity | newLimit < 0)
throw createLimitException(newLimit);
limit = newLimit;
if (position > limit) position = limit;
if (mark > limit) mark = -1;
return this;
}
mark
public Buffer mark() {
//标記mark為目前position
mark = position;
return this;
}
将目前位置做标記,在使用reset方法時,可以回到目前mark的位置
reset
public Buffer reset() {
int m = mark;
if (m < 0)
throw new InvalidMarkException();
//設定position為目前mark
position = m;
return this;
}
回到之前設定mark的位置
clear
public Buffer clear() {
//設定position為0
position = 0;
//limit設定為capacity大小
limit = capacity;
//mark設定為-1(初始化)
mark = -1;
return this;
}
讀取完資料後調用clear,即将buffer邏輯上清空了,可以從0開始寫入資料
flip
public Buffer flip() {
//limit設定為目前位置
limit = position;
//position設定為0
position = 0;
//mark設定為-1(初始化)
mark = -1;
return this;
}
将buffer從寫模式設定為讀模式,limit設定為目前position的位置,即隻能讀取limit大小的資料
rewind
public Buffer rewind() {
position = 0;
mark = -1;
return this;
}
将position設定為0,即從頭開始讀取
remaining
public final int remaining() {
return limit - position;
}
傳回buffer中還有多少byte是未讀的
hasRemaining
public final boolean hasRemaining() {
return position < limit;
}
是否已讀完
compact
public ByteBuffer compact() {
System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
position(remaining());
limit(capacity());
discardMark();
return this;
}
将position和limit直接的資料copy到byteBuffer的起始處,将已讀資料清空,并将新的position設定為目前未讀資料的末尾。這樣能避免clear方法會将未讀資料也清空的問題
slice
public ByteBuffer slice() {
return new HeapByteBufferR(hb,
-1,
0,
this.remaining(),
this.remaining(),
this.position() + offset);
}
ByteBuffer slice(int pos, int lim) {
assert (pos >= 0);
assert (pos <= lim);
int rem = lim - pos;
return new HeapByteBufferR(hb,
-1,
0,
rem,
rem,
pos + offset);
}
新建立一個ByteBuffer,将緩存區分片,設定一個子緩沖區,實際上記憶體還是共享的,資料發生改變,兩個緩沖區讀取的資料都會是改變後的。
總結
Buffer最重要的三個屬性:position、limit、capacity。牢記這三個屬性的含義及讀寫切換時,設定值是如何變化的,Buffer的核心知識點就掌握了。