Java中的NIO學習(一、緩沖區與Buffer)


這里有一個很好的NIO系列學習教程: http://ifeve.com/overview/

看完之后感覺主要就講了以下一些東西。

Channel 通道

Buffer 緩沖區

Selector 選擇器

其中Channel對應以前的流,Buffer不是什么新東西,Selector是因為nio可以使用異步的非堵塞模式才加入的東西。

以前的流總是堵塞的,一個線程只要對它進行操作,其它操作就會被堵塞,也就相當於水管沒有閥門,你伸手接水的時候,不管水到了沒有,你就都只能耗在接水(流)上。

nio的Channel的加入,相當於增加了水龍頭(有閥門),雖然一個時刻也只能接一個水管的水,但依賴輪換策略,在水量不大的時候,各個水管里流出來的水,都可以得到妥善接納,這個關鍵之處就是增加了一個接水工,也就是Selector,他負責協調,也就是看哪根水管有水了的話,在當前水管的水接到一定程度的時候,就切換一下:臨時關上當前水龍頭,試着打開另一個水龍頭(看看有沒有水)。

當其他人需要用水的時候,不是直接去接水,而是事前提了一個水桶給接水工,這個水桶就是Buffer。也就是,其他人雖然也可能要等,但不會在現場等,而是回家等,可以做其它事去,水接滿了,接水工會通知他們。

這其實也是非常接近當前社會分工細化的現實,也是統分利用現有資源達到並發效果的一種很經濟的手段,而不是動不動就來個並行處理,雖然那樣是最簡單的,但也是最浪費資源的方式。

 

下面進入正題

在整個java的IO中,大部分操作都屬於阻塞性操作,例如,鍵盤輸入數據,程序必須一直等待輸入數據,否則程序無法向下繼續執行,還有就是網絡中Socket程序必須通過accept()方法一直等待用戶的連接。這樣一來,勢必造成大量系統資源的浪費。所以JAVA在jdk1.4之后增加了新IO,(說新,現在一點都不新了啊),NIO的操作基本上都是使用緩沖區完成的。

既然使用了緩沖區,那么操作的性能將是最高的。

緩沖區Buffer

在基本IO操作中,所有的數據都是以流的形式操作的,而在NIO中,則都是使用緩沖區,所有的讀寫操作都是使用緩沖區完成的。緩沖區本質上是一塊可以寫入數據,然后可以從中讀取數據的內存。這塊內存被包裝成NIO Buffer對象,並提供了一組方法,用來方便的訪問該塊內存。

java.nio.Buffer下有七個子類,這些子類緩沖區分別用於存儲不同類型的數據。

分別是:ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer, LongBuffer, ShortBuffer ,都是針對基本數據類型,沒有針對布爾型的。

(在ByteBuffer下還有一個MappedByteBuffer,用於表示內存映射文件)

 

在Buffer中的函數

public final int capacity()              //Returns this buffer's capacity.

public final int position()              //Returns this buffer's position.

public final Buffer position(int newPosition)      //Sets this buffer's position. If the mark is defined and larger than the new position then it is discarded.

public final int limit()              //Returns this buffer's limit.

public final Buffer limit(int newLimit)       //Sets this buffer's limit. If the position is larger than the new limit then it is set to the new limit. If the mark is defined and larger than the new                           limit then it is discarded.

public final Buffer flip()            //Flips this buffer. The limit is set to the current position and then the position is set to zero. 

public final int remaining()          //Returns the number of elements between the current position and the limit.

……

capacity的含義總是一樣的,返回的是緩沖區的大小。而position和limit的含義取決於Buffer處在讀模式還是寫模式。

capacity

作為一個內存塊,Buffer有一個固定的大小值,也叫"capacity".你只能往里寫capacity個byte、long,char等類型。一旦Buffer滿了,需要將其清空(通過讀數據或者清除數據)才能繼續寫數據往里寫數據。

position

當你寫數據到Buffer中時,position表示當前可寫入的位置,既此指針永遠放到寫入的最后一個元素之后(例如,你已經寫入四個元素,那么此指針將指向第五個位置)。初始的position值為0.當一個byte、long等數據寫到Buffer后, position會向前移動到下一個可插入數據的Buffer單元。position最大可為capacity – 1.(其實就是一個數組)

當讀取數據時,也是從某個特定位置讀。當將Buffer從寫模式切換到讀模式,position會被重置為0. 當從Buffer的position處讀取數據時,position向前移動到下一個可讀的位置。

limit

在寫模式下,Buffer的limit表示你最多能往Buffer里寫多少數據。 寫模式下,limit等於Buffer的capacity。

當切換Buffer到讀模式時, limit表示你最多能讀到多少數據。因此,當切換Buffer到讀模式時,limit會被設置成寫模式下的position值。換句話說,你能讀到之前寫入的所有數據(limit被設置成已寫數據的數量,這個值在寫模式下就是position)

flip()方法

flip方法將Buffer從寫模式切換到讀模式。調用flip()方法會將position設回0,並將limit設置成之前position的值。

換句話說,position現在用於標記讀的位置,limit表示之前寫進了多少個byte、char等 —— 現在能讀取多少個byte、char等。

 

下面通過一段簡單代碼看看它的功能。

import java.nio.IntBuffer;

public class IntBufferDemo {
    public static void main(String[] args) {
        IntBuffer buf = IntBuffer.allocate(10) ;    // 准備出10個大小的緩沖區
        System.out.print("1、寫入數據之前的position、limit和capacity:") ;
        System.out.println("position = " + buf.position() + ",limit = " + buf.limit() + ",capacty = " + buf.capacity()) ;
        int temp[] = {5,7,9} ;              // 定義一個int數組
        buf.put(3) ;                        // 設置一個數據
        buf.put(temp) ;                     // 此時已經存放了四個記錄
        System.out.print("2、寫入數據之后的position、limit和capacity:") ;
        System.out.println("position = " + buf.position() + ",limit = " + buf.limit() + ",capacty = " + buf.capacity()) ;

        buf.flip() ;                        // 重設緩沖區  postion = 0 ,limit = 原本position
        System.out.print("3、准備輸出數據時的position、limit和capacity:") ;
        System.out.println("position = " + buf.position() + ",limit = " + buf.limit() + ",capacty = " + buf.capacity()) ;
        System.out.print("緩沖區中的內容:") ;
        while(buf.hasRemaining()){           //如果緩沖區中還有內容
            int x = buf.get() ;
            System.out.print(x + "、") ;
        }
    }
}

輸出結果:    緩沖區中的內容:3、5、7、9

緩沖區操作細節如下圖所示:

A:開辟緩沖區

 

B:向緩沖區中增加一個數據

 

C:向緩沖區中增加一組數據

 

D:執行flip()方法,limit設置為position,position設置為0

注:0 <= position <= limit <= capacity

 

 

另一些其他的重要方法:也可直接查看原API文檔   http://docs.oracle.com/javase/8/docs/api/java/nio/Buffer.html 

rewind()方法

Buffer.rewind()將position設回0,所以你可以重讀Buffer中的所有數據。limit保持不變,仍然表示能從Buffer中讀取多少個元素(byte、char等)。

clear()與compact()方法 

一旦讀完Buffer中的數據,需要讓Buffer准備好再次被寫入。可以通過clear()或compact()方法來完成。 

如果調用的是clear()方法,position將被設回0,limit被設置成 capacity的值。換句話說,Buffer 被清空了。Buffer中的數據並未清除,只是這些標記告訴我們可以從哪里開始往Buffer里寫數據。 

如果Buffer中有一些未讀的數據,調用clear()方法,數據將“被遺忘”,意味着不再有任何標記會告訴你哪些數據被讀過,哪些還沒有。 

如果Buffer中仍有未讀的數據,且后續還需要這些數據,但是此時想要先寫些數據,那么使用compact()方法。

compact()方法將所有未讀的數據拷貝到Buffer起始處。然后將position設到最后一個未讀元素正后面。limit屬性依然像clear()方法一樣,設置成capacity。現在Buffer准備好寫數據了,但是不會覆蓋未讀的數據。

mark()與reset()方法 

通過調用Buffer.mark()方法,可以標記Buffer中的一個特定position。之后可以通過調用Buffer.reset()方法恢復到這個position。

 

也可創建只讀緩沖區 buf.asReadOnlyBuffer()

public static void main(String args[]){
        IntBuffer buf = IntBuffer.allocate(10) ;    // 准備出10個大小的緩沖區
        IntBuffer read = null ;    // 定義子緩沖區
        for(int i=0;i<10;i++){
            buf.put(2 * i + 1) ;    // 在主緩沖區中加入10個奇數
        }
        read = buf.asReadOnlyBuffer()  ;// 創建只讀緩沖區
        


        read.flip() ;    // 重設緩沖區
        System.out.print("主緩沖區中的內容:") ;
        while(read.hasRemaining()){
            int x = read.get() ;
            System.out.print(x + "、") ;
        }
        read.put(30) ;    // 修改,錯誤
    }

 

大部分放的部分都是從網上直接抄的, 代碼部分是自己寫的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM