Java NIO使用分析

1、概述

傳統的Java io是面向流Stream的,阻塞IO;而nio則是面向緩衝區buffer的,非阻塞的io。區別是流中數據不能移動,而buffer可以移動。

Java nio選擇器Selectors允許單個線程監控多個通道的輸入和輸出。

Java緩衝區Buffer,本質上其實是一個內存塊,可以往裏面寫入數據,可以從裏面讀取數據。其被包裝成NIO buffer對象,便於操作。
Buffer 一般和Channel配合使用。

一般使用步驟如下:

1.數據寫入緩衝區,緩衝區會記錄寫入數據的大小。
2.調用flip方法,從寫入模式轉至讀取模式。
3.從緩衝區讀取數據
4.調用clear或者compact方法清除數據

2 Channel

2.1 channel與stram區別

channel 既可以讀也可以寫,而stream只能讀或者寫二選一。
channel可以異步讀寫
channel只能從buffer中進行讀寫

2.2 常見channel

FileChannel 從文件中讀取數據

DatagramChannel 從UDP中進行讀寫

SocketChannel 從TCP中進行讀寫

ServerSocketChannel 允許監聽進來的TCP連接,每個進來的連接都會創建一個SocketChannel

2.3用法

xxx.getChannel()方法獲取一個Channel,然後和buffer配合讀寫數據。

3 Buffer容量、位置、限制

capacity:容量,buffer的大小。創建時確定,無法更改。
position:位置,被讀取或寫入的下一個元素的位置。
    寫模式:寫入數據的位置,默認爲0,最大可以是capacity-1
    讀模式:從position開始讀取,然後position會自動增長,指向下一個讀取位置。
        從寫模式切換到讀模式時,會將position重置爲0.
        可以通過position(int)方法重設position
limit:限制,緩衝區中可以使用的元素個數。
    寫模式:limit 等於capacity,表示你可以寫多少數據到緩衝區,l
    讀模式:limit表示你可以從緩衝區讀取多少數據。
    從寫模式切換到讀模式的時候,limit的值爲寫模式的position的值。
    可以通過limit(int)方法重設limit
Mark 標記:
    被記錄的位置,調用mark方法時設置(mark=position),默認爲-1

buffer實際存儲的對象時數組,其是非線程安全的

4 NIO buffer類型

java nio緩衝區有以下幾種:

ByteBuffer
MappedByteBuffer
CharBuffer
DoubleBuffer
FloatBuffer
IntBuffer
LongBuffer
ShortBuffer

5 緩衝區的使用

5.1 創建

創建完成後,默認就是寫模式。

ByteBuffer.allocate(int size) 在JVM中分配size字節大小的容量。
ByteBuffer. allocateDirect(int size) 在JVM之外,系統內存中分配size字節大小的的容量。
XXXBuffer.wrap(byte[] array) 使用已經分配的數組作爲buffer管理。

5.2寫緩衝區

方式一:從通道Channel裏面寫數據到緩衝區。

RandomAccessFile file = new RandomAccessFile("data.txt", "rw");
FileChannel fChannel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(48);
//從fChannel中讀取數據寫入到buffer中,byteReads是返回的寫入數據的大小,-1表示寫完所有數據
int byteReads = fChannel.read(buffer);

方式二:使用buffer的putXXX方法

RandomAccessFile file = new RandomAccessFile("data.txt", "rw");
FileChannel fChannel = file.getChannel();
ByteBuffer buffer = ByteBuffer.allocate(48);
buf.put(127);

put方法有很多重載方式,支持各種方式的寫入數據,具體可以查看Api。單參數的put方法會自動調整position的值,帶有index參數的put,如put(int index, byte b)並導致任何屬性的變化。

5.3 flip

flip方法用於切換緩衝區從寫模式到讀模式。

調用flip 方法會講position設置爲0,同時將limit設置爲之前的position的值。

5.4 讀緩衝區

方式一:將數據從buffer讀取到channel

//從buffer讀取數據到channel,返回值byteReads爲讀取的數據大小,-1表示讀取完所有數據。
byteReads = fChannel.read(buffer);  

方式二:使用getXXX方法讀取數據

getXXX方法同put方法類似

buf.get()

可以使用rewind()方法,將position設置爲0,然後重新從buffer讀取數據。get()會導致position的變化,get(int index) 不會導致任何屬性的變化。

5.5 數據清除

讀取完數據後,若需要再次寫入,可以調用clearcompact方法清除數據

clear方法將position設置爲0,將limit設置爲capacity,實際並沒有清空數據,只是重置了寫入的位置。

compact方法將未讀取的數據移動到緩衝區開始位置,將position設置在最後的未讀元素後面,limit仍然設置爲capacity。compact使得buffer可以再次寫入,append到之前未讀數據後面。

5.6 設置標記

mark()方法可以在buffer中設置一個標記,記錄當前position,然後若想回到這個標記的位置調用reset()即可。

5.7 buffer之間的比較

equals()比較buffer中剩餘部分元素,其判斷相等的條件:

1、兩個buffer有數據相同的類型
2、buffer剩餘元素的數目相等
3、buffer剩餘元素一一相等

compareTo():比較兩個buffer的剩餘元素,bufferA比bufferB小的情況如下:

1、bufferA的元素小於bufferB中對應位置的元素
2、所有元素相等,但bufferA比bufferB元素個數少。

5.8 order方法

獲取以及設置讀寫的字節序。

6 Scatter/Gather

Java NIO支持scatter/gather(分散/聚集),用於描述從通道讀取和寫入通道。使用場景:消息數據各部分需要分開傳輸,比如消息頭和消息體組成的消息。

分散讀是在讀操作的時候,讀取的數據被寫入多個buffer中。scatter將數據從通道寫入多個buffer。

聚集寫,在寫入多個buffer到一個通道,此時gather將數據從多個buffer寫入通道。

6.1 read

read 把通道里的數據按照buffers數組中的順序填充到相應的buffer中。當第一個buffer填充滿後,纔會填充下一個buffer。

ByteBuffer head = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] buffers = {head, body};    
channel.read(buffers);

6.2 write

write方法按照數組中的順序,一個個的講buffer中的內容寫入Channel中。而且寫入的時候是僅僅將buffer的position和limit之間的元素寫入。而不是全部寫入。

ByteBuffer head = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] buffers = {head, body};    
channel.write(buffers);
發佈了46 篇原創文章 · 獲贊 66 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章