詳細了解JAVA NIO之Buffer(緩沖區)
當我們需要與 NIO Channel 進行交互時, 我們就需要使用到 NIO Buffer, 即數據從 Buffer讀取到 Channel 中, 并且從 Channel 中寫入到 Buffer 中。緩沖區本質上是一塊可以寫入數據,然后可以從中讀取數據的內存。這塊內存被包裝成NIO Buffer對象,并提供了一組方法,用來方便的訪問該塊內存。
緩沖區基礎
Buffer 類型有:
緩沖區是包在一個對象內的基礎數據的數組,Buffer類相比一般簡單數組而言其優點是將數據的內容和相關信息放在一個對象里面,這個對象提供了處理緩沖區數據的豐富的API。
所有緩沖區都有4個屬性:capacity、limit、position、mark,并遵循:capacity>=limit>=position>=mark>=0,下面是對這4個屬性的解釋:
Capacity: 容量,即可以容納的最大數據量;在緩沖區創建時被設定并且不能改變 Limit: 上界,緩沖區中當前數據量 Position: 位置,下一個要被讀或寫的元素的索引 Mark: 標記,調用mark()來設置mark=position,再調用reset()可以讓position恢復到標記的位置即position=mark我們通過一個簡單的操作流程來說明buffer的使用,下圖是新創建的容量為10的緩沖區邏輯視圖:
然后進行5次調用put:
buffer.put((byte)’A’).put((byte)’B’).put((byte)’C’).put((byte)’D’).put((byte)’E’)
5次調用put之后的緩沖區為:
現在緩沖區滿了,我們必須將其清空。我們想把這個緩沖區傳遞給一個通道,以使內容能被全部寫出,但現在執行get()無疑會取出未定義的數據。我們必須將 posistion設為0,然后通道就會從正確的位置開始讀了,但讀到哪算讀完了呢?這正是limit引入的原因,它指明緩沖區有效內容的未端。這個操作 在緩沖區中叫做翻轉:buffer.flip()。
Buffer的基本用法
使用Buffer讀寫數據一般遵循以下四個步驟:
寫入數據到Buffer 調用flip()方法 從Buffer中讀取數據 調用clear()方法或者compact()方法當向buffer寫入數據時,buffer會記錄下寫了多少數據。
一旦要讀取數據,需要通過flip()方法將Buffer從寫模式切換到讀模式。在讀模式下,可以讀取之前寫入到buffer的所有數據。
一旦讀完了所有的數據,就需要清空緩沖區,讓它可以再次被寫入。有兩種方式能清空緩沖區:調用clear()或compact()方法。clear()方法會清空整個緩沖區。compact()方法只會清除已經讀過的數據。任何未讀的數據都被移到緩沖區的起始處,新寫入的數據將放到緩沖區未讀數據的后面。
下面我們看一段程序來看一下Buffer的基本用法:
public static void readFile(String fileName) { RandomAccessFile aFile = null; try { //文件流 aFile = new RandomAccessFile(fileName, 'rw'); //將文件輸入到管道 FileChannel inChannel = aFile.getChannel(); //為buffer分配1024個字節大小的空間 ByteBuffer buf = ByteBuffer.allocate(1024); //將buffer中的內容讀取到管道中 int bytesRead = inChannel.read(buf); while (bytesRead != -1) { //反轉buffer,將寫模式改為讀模式 buf.flip(); while (buf.hasRemaining()) {//獲取buffer中的數據System.out.print((char) buf.get()); } //將上次分配的1024字節的內容清空,為下次接收做準備 buf.clear(); //管道重新讀取buffer中的內容 bytesRead = inChannel.read(buf); } aFile.close(); } catch (Exception e) { e.printStackTrace(); } }
字節緩沖區
我們將進一步觀察字節緩沖區。所有的基本數據類型都有相應的緩沖區類(布爾型除外),但字節緩沖區有自己的獨特之處。字節是操作系統及其I/O設備使用的基本數據類型。當在JVM和操作系統間傳遞數據時,將其他的數據類型拆分成構成它們的字節是十分必要的。如我們在后面的章節中將要看到的那樣,系統層次的I/O面向字節的性質可以在整個緩沖區的設計以及它們互相配合的服務中感受到。
直接緩沖區
我們知道操作系統是在內存中進行I/O操作,這些內存區域,就操作系統方面而言,是相連的字節序列。于是,毫無疑問,只有字節緩沖區有資格參與I/O操作。即操作系統會直接存取進程,那么我們現在在JVM中進行操作,java中的內存空間是由JVM直接進行管理,但是在JVM中,字節數組可能不會在內存中連續存儲,或者無用存儲單元收集可能隨時對其進行移動,這就不能保證I/O操作的目標是連續的。
出于這一原因,引入了直接緩沖區的概念。直接緩沖區被用于與通道和固有I/O例程交互。它們通過使用固有代碼來告知操作系統直接釋放或填充內存區域,對用于通道直接或原始存取的內存區域中的字節元素的存儲盡了最大的努力。
直接字節緩沖區通常是I/O操作最好的選擇。在設計方面,它們支持JVM可用的最高效I/O機制。非直接字節緩沖區可以被傳遞給通道,但是這樣可能導致性能損耗。通常非直接緩沖不可能成為一個本地I/O操作的目標。如果您向一個通道中傳遞一個非直接ByteBuffer對象用于寫入,通道可能會在每次調用中隱含地進行下面的操作:
創建一個臨時的直接ByteBuffer對象。 將非直接緩沖區的內容復制到臨時緩沖中。 使用臨時緩沖區執行低層次I/O操作。 臨時緩沖區對象離開作用域,并最終成為被回收的無用數據。視圖緩沖區
就像我們已經討論的那樣,I/O基本上可以歸結成組字節數據的四處傳遞。在進行大數據量的I/O操作時,很又可能你會使用各種ByteBuffer類去讀取文件內容,接收來自網絡連接的數據,等等。一旦數據到達了你的ByteBuffer,您就需要查看它以決定怎么做或者在將它發送出去之前對它進行一些操作。ByteBuffer類提供了豐富的API來創建視圖緩沖區。
視圖緩沖區通過已存在的緩沖區對象實例的工廠方法來創建。這種視圖對象維護它自己的屬性,容量,位置,上界和標記,但是和原來的緩沖區共享數據元素。但是ByteBuffer類允許創建視圖來將byte型緩沖區字節數據映射為其它的原始數據類型。例如,asLongBuffer()函數創建一個將八個字節型數據當成一個long型數據來存取的視圖緩沖區。
但是使用視圖緩沖區的話,一旦ByteBuffer對于視圖的維護對象產生非常規行的使用,那么對于工廠方法創建的緩沖區而言,asLongBuffer()函數就不在使用這個視窗,那么這個8字節的數據當成一個long類型的數據類型來存取的數據視圖。
以上就是詳細了解JAVA NIO之Buffer(緩沖區)的詳細內容,更多關于JAVA NIO buffer(緩沖區)的資料請關注好吧啦網其它相關文章!
相關文章: