Java NIO(一)緩衝區Buffer
Java NIO全稱Java non-blocking IO,是指jdk1.4 及以上版本里提供的新api(New IO) ,為所有的原始型別(Boolean型別除外)提供快取支援的資料容器,使用它可以提供非阻塞式的高伸縮性網路。
下表總結了Java IO和NIO之間的主要區別:
IO | NIO |
面向流 | 面向緩衝 |
阻塞IO | 非阻塞IO |
無 | 選擇器 |
Java NIO系統的核心在於:通道(Channel)和緩衝區(Buffer)。通道表示開啟到 IO 裝置(例如:檔案、套接字)的連線。若需要用 NIO 系統,需要獲取用於連線 IO 裝置的通道以及用於容納資料的緩衝區。然後操作緩衝區,對資料進行處理。簡而言之,Channel 負責傳輸, Buffer 負責儲存
緩衝區:一個用於特定基本資料型別的容器。由 java.nio 包定義的,所有緩衝區
都是 Buffer 抽象類的子類。在 Java NIO 中負責資料的存取。緩衝區就是陣列。用於儲存不同資料型別的資料
Java NIO 中的 Buffer 主要用於與 NIO 通道進行互動,資料是從通道讀入緩衝區,從緩衝區寫入通道中的。
根據資料型別不同(boolean 除外),提供了相應型別的緩衝區:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer
上述緩衝區的管理方式幾乎一致,通過 allocate() 獲取緩衝區
緩衝區存取資料的兩個核心方法:
put() : 存入資料到緩衝區中
get() : 獲取緩衝區中的資料
緩衝區的基本屬性
Buffer 中的重要概念:
1. 容量 (capacity) :表示 Buffer 最大資料容量,緩衝區容量不能為負,並且建立後不能更改。
2. 限制 (limit) :第一個不應該讀取或寫入的資料的索引,即位於 limit 後的資料不可讀寫。緩衝區的限制不能為負,並且不能大於其容量。
3. 位置 (position): :下一個要讀取或寫入的資料的索引。緩衝區的位置不能為負,並且不能大於其限制
4. 標記 (mark) 與重置 (reset) :標記是一個索引,通過 Buffer 中的 mark() 方法指定 Buffer 中一個特定的 position,之後可以通過呼叫 reset() 方法恢復到這個 position.
標記 、 位置 、 限制 、 容量遵守以下不變式: 0 <= mark <= position <= limit <= capacity
Buffer 的常用方法描述:
Buffer clear() 清空緩衝區並返回對緩衝區的引用
Buffer flip() 為 將緩衝區的界限設定為當前位置,並將當前位置充值為 0 0
int capacity() 返回 Buffer 的 capacity 大小
boolean hasRemaining() 判斷緩衝區中是否還有元素
int limit() 返回 Buffer 的界限(limit) 的位置
Buffer limit(int n) 將設定緩衝區界限為 n, 並返回一個具有新 limit 的緩衝區物件
Buffer mark() 對緩衝區設定標記
int position() 返回緩衝區的當前位置 position
Buffer position(int n) 將設定緩衝區的當前位置為 n , 並返回修改後的 Buffer 物件
int remaining() 返回 position 和 limit 之間的元素個數
Buffer reset() 將位置 position 轉到以前設定的 mark 所在的位置
Buffer rewind() 將位置設為為 0, 取消設定的 mark
緩衝區的資料操作
Buffer 所有子類提供了兩個用於資料操作的方法:get()與 put() 方法
1. 獲取 Buffer 中的資料
get() :讀取單個位元組
get(byte[] dst):批量讀取多個位元組到 dst 中
get(int index):讀取指定索引位置的位元組(不會移動 position)
2. 放 入資料到 Buffer 中
put(byte b):將給定單個位元組寫入緩衝區的當前位置
put(byte[] src):將 src 中的位元組寫入緩衝區的當前位置
put(int index, byte b):將指定位元組寫入緩衝區的索引位置(不會移動 position)
@Test
public void test01(){
//分配緩衝區
System.out.println("=======allocate=======");
String str="1234";
ByteBuffer buf = ByteBuffer.allocate(10);
System.out.println(buf.capacity());//10
System.out.println(buf.limit());//10
System.out.println(buf.position());//0
//存入資料
buf.put(str.getBytes());
System.out.println("=======buf===========");
System.out.println(buf.capacity());//10
System.out.println(buf.limit());//10
System.out.println(buf.position());//4
//切換緩衝區讀取資料
System.out.println("=======flip==========");
buf.flip();
System.out.println(buf.capacity());//10
System.out.println(buf.limit());//4
System.out.println(buf.position());//0
//獲取緩衝區資料
System.out.println("=======get==========");
byte[] dst=new byte[buf.limit()];
buf.get(dst);
String getStr=new String(dst, 0, dst.length);
System.out.println(getStr);//1234
System.out.println(buf.capacity());//10
System.out.println(buf.limit());//4
System.out.println(buf.position());//4
//rewind-重複讀資料
System.out.println("=======rewind==========");
buf.rewind();
System.out.println(buf.capacity());//10
System.out.println(buf.limit());//4
System.out.println(buf.position());//0
//clear-清空緩衝區 但是緩衝區中的資料依然存在,但是處於“被遺忘”狀態,引數指標等被清空,
System.out.println("========clear==========");
buf.clear();
System.out.println(buf.capacity());//10
System.out.println(buf.limit());//10
System.out.println(buf.position());//0
//測試clear之後資料依然存在
System.out.println("========clear之後=======");
char b = (char) buf.get();
System.out.println(b);//1
System.out.println("========再次put=======");
String str2="23";
buf.put(str2.getBytes());
System.out.println(buf.capacity());//10
System.out.println(buf.limit());//10
System.out.println(buf.position());//3
//獲取緩衝區資料
System.out.println("=======get==========");
buf.flip();
byte[] dst2=new byte[buf.limit()];
buf.get(dst2);
String getStr2=new String(dst2, 0, dst2.length);
System.out.println(getStr2);//123
System.out.println(buf.capacity());//10
System.out.println(buf.limit());//3
System.out.println(buf.position());//3
//clear資料並未清除,只是那幾個屬性值恢復了
}
@Test
public void test02(){
String str="1234567";
ByteBuffer buffer = ByteBuffer.allocate(1024);
buffer.put(str.getBytes());
buffer.flip();
byte[] dst=new byte[buffer.limit()];
buffer.get(dst, 0, 2);
System.out.println(new String(dst, 0, 2));//12
System.out.println(buffer.position());//2
//mark() : 標記
buffer.mark();
/*byte[] dst2=new byte[10];*/
buffer.get(dst, 2, 2);
System.out.println(new String(dst, 2, 2));//34
System.out.println(buffer.position());//4
//reset() : 恢復到 mark 的位置
buffer.reset();
System.out.println(buffer.position());//2
//判斷緩衝區中是否還有剩餘資料
if(buffer.hasRemaining()){
//獲取緩衝區中可以操作的數量
System.out.println(buffer.remaining());//5(數量)
}
}
直接與非直接緩衝區
1 位元組緩衝區要麼是直接的,要麼是非直接的。如果為直接位元組緩衝區,則 Java 虛擬機器會盡最大努力直接在
機 此緩衝區上執行本機 I/O 操作。也就是說,在每次呼叫基礎作業系統的一個本機 I/O 操作之前(或之後),
虛擬機器都會盡量避免將緩衝區的內容複製到中間緩衝區中(或從中間緩衝區中複製內容)。
2. 直接位元組緩衝區可以通過呼叫此類的 allocateDirect() 工廠方法 來建立。此方法返回的 緩衝區進行分配和取消
分配所需成本通常高於非直接緩衝區 。直接緩衝區的內容可以駐留在常規的垃圾回收堆之外,因此,它們對
應用程式的記憶體需求量造成的影響可能並不明顯。所以,建議將直接緩衝區主要分配給那些易受基礎系統的
機 本機 I/O 操作影響的大型、持久的緩衝區。一般情況下,最好僅在直接緩衝區能在程式效能方面帶來明顯好
處時分配它們。
3. 直接位元組緩衝區還可以過 通過FileChannel 的 map() 方法 將檔案區域直接對映到記憶體中來建立 。該方法返回
MappedByteBuffer 。Java 平臺的實現有助於通過 JNI 從本機程式碼建立直接位元組緩衝區。如果以上這些緩衝區
中的某個緩衝區例項指的是不可訪問的記憶體區域,則試圖訪問該區域不會更改該緩衝區的內容,並且將會在
訪問期間或稍後的某個時間導致丟擲不確定的異常。
4. 位元組緩衝區是直接緩衝區還是非直接緩衝區可通過呼叫其 isDirect() 方法來確定。提供此方法是為了能夠在
效能關鍵型程式碼中執行顯式緩衝區管理 。
非直接緩衝區:通過 allocate() 方法分配緩衝區,將緩衝區建立在 JVM 的記憶體中
直接緩衝區:通過 allocateDirect() 方法分配直接緩衝區,將緩衝區建立在實體記憶體中。可以提高效率
@Test
public void test03(){
//分配直接緩衝區
ByteBuffer allocateDirect = ByteBuffer.allocateDirect(1024);
System.out.println(allocateDirect.isDirect());//true
}
原文:https://blog.csdn.net/yjaspire/article/details/80474856