NIO(一)——Buffer
- NIO即New IO,是用來代替標準IO的,提供了與標準IO完全不同傳輸方式。
- 核心:通道(Channel)和緩衝區(Buffer)和選擇器(Selectors),Channel負責傳輸,Buffer負責存儲
- 標準IO是面向字節流的,NIO是面向緩衝區的
- 標準IO是阻塞IO,NIO是非阻塞IO
- 當線程從通道讀取數據到緩衝區時,線程還是可以進行其他事情。當數據被寫入到緩衝區時,線程可以繼續處理它。從緩衝區寫入通道也類似。
NIO具有選擇器Selectors
- 選擇器用於監聽多個通道的事件(比如:連接打開,數據到達)。因此,單個的線程可以監聽多個數據通道
- 緩衝區Buffer
- 在Java NIO中負責數據的存取。緩衝區本質上是一塊可以寫入數據,然後可以從中讀取數據的內存。這塊內存被包裝成NIO Buffer對象,並提供了一組方法,用來方便的訪問該塊內存。
- 根據數據類型的不同,提供了不同類型的緩衝區(boolean除外):
- ByteBuffer
- ShortBuffer
- IntBuffer
- LongBuffer
- FloatBuffer
- DoubleBuffer
- CharBuffer
- 上述緩衝區的管理方式幾乎一致,通過allocate()獲取緩衝區:
- ByteBuffer buf = ByteBuffer.allocate(1024);
緩衝區存儲數據的兩個核心方法:
- put():存數據到緩衝區
- get():從緩衝區取數據
緩衝區中的核心屬性:
- capacity:容量,表示緩衝區中最大存儲數據的容量,一旦聲明不可改變
- limit:界限,表示緩存區衝可以操作數據的大小。limit後的數據不能進行讀寫(讀和寫操作含義不同)
- position:位置,表示緩衝區中正在操作的數據的位置
- mark:標記,表示記錄當前position的位置,可以通過reset()恢復到mark的位置
- 0<=mark<=position<=limit<=capacity
- 方法:
-
- flid()方法:在切換讀寫模式的時候必須調用flid()方法
- buf.flip();
- flip()方法:
public final Buffer flip() {
limit = position;
position = 0;
mark = -1;
return this;
}
- 可重複讀數據,將position置0,limit不變
public final Buffer rewind() {
position = 0;
mark = -1;
return this;
}
clear()與compact()方法:- 一旦讀完緩衝區中的數據,需要讓緩衝區準備好再次被寫入。可以通過clear()或compact方法來完成。
- 如果調用的是clear()方法,position將被置0,limit被設置爲capacity的值。即緩衝區被清空。但緩衝區中的數據並未被清除。
- 如果緩衝區中有一些未讀的數據,調用clean()方法,數據將“被遺忘”。
- 如果緩衝區中有一些未讀的數據,且後續還需要這些數據,但此時想先寫些數據,就使用compact()方法。compact()方法將所有未讀的數據拷貝到Buffer起始處,然後將position設到最後一個未讀元素後面,limit=capacity。
public final Buffer clear() {
position = 0;
limit = capacity;
mark = -1;
return this;
}
public abstract ByteBuffer compact();
- 非直接緩衝區:通過allocate()方法分配緩衝區,將緩衝區建立在JVM內存中
public static ByteBuffer allocate(int capacity) {
if (capacity < 0)
throw new IllegalArgumentException();
return new HeapByteBuffer(capacity, capacity);
}
HeapByteBuffer(int cap, int lim) {
super(-1, 0, lim, cap, new byte[cap], 0);
}
ByteBuffer(int mark, int pos, int lim, int cap, byte[] hb, int offset){
super(mark, pos, lim, cap);
this.hb = hb;
this.offset = offset;
}
- 直接緩衝區:
- 通過allocateDirect()方法分配直接緩衝區,將緩衝區建立在物理內存中。可以提高效率
public static ByteBuffer allocateDirect(int capacity) {
return new DirectByteBuffer(capacity);
}
DirectByteBuffer(int cap) {
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 {
base = unsafe.allocateMemory(size);
} catch (OutOfMemoryError x) {
Bits.unreserveMemory(size, cap);
throw x;
}
unsafe.setMemory(base, size, (byte) 0);
if (pa && (base % ps != 0)) {
address = base + ps - (base & (ps - 1));
} else {
address = base;
}
cleaner = Cleaner.create(this, new Deallocator(base, size, cap));
att = null;
}