天天看點

Java NIO---buffer部分

Java NIO---buffer部分

最近想建一個網絡傳輸的平台,檢視到了Jboss 的Netty,而他們核心的傳輸是用了JDK 1.4以後的

NIO特性,是以借機會學習一下NIO

NIO主要有下面幾大部分

Buffer:Io的操作少不了緩存,通過Buffer能大大提高傳輸的效率,同樣NIO中也有Buffer的這部分

Buffer針對資料類型有相應的子類,他們都是繼承Buffer class

比如CharBuffer,IntBuffer等等

需要說明的子類MappedByteBuffer 通過命名可以看出這個MappedByteBuffer有mapped+byte+Buffer組成

據我了解 這麼mapped 是指memory-mapped

這裡解釋一下memory-mapped, 以為我們說的buffer 可能就是指實體上的記憶體,包括jdk 以前的io

一般情況下,buffer指實體上的記憶體沒什麼問題,可以實體記憶體畢竟比較有限,當需要很大buffer存放資料的時候

實體記憶體就不夠,那麼就引出一個虛拟記憶體的概念,就像現在的os 一樣,也有虛拟記憶體的概念,虛拟記憶體本質上

是存在硬碟上的,隻是實體記憶體存放的不是具體的資料而是虛拟記憶體上的位址,而具體的資料則是在虛拟記憶體上,這樣

實體記憶體隻是存放很多位址,這樣就大大增加了buffer的size。當然如果你buffer的資料很小,也可以知道放入實體記憶體

是以我認為 這個memory-mapped 是指的用虛拟記憶體的哪種緩存模式

A.Buffer的 attribute

1.Capacity:buffer的大小,一個buffer按照某個Capacity建立後就不能修改

2.Limit:用了多少buffer

3.Position:指針,擷取下一個元素的位置,操作get(),put(),這個position會自動更新(和iterator相似)

4.Mark:被标記的指針位置 通過mark()操作可以使mark = position 通過reset() 可以使position = mark

上面這幾個屬性應該符合下面條件

0 <= mark <= position <= limit <= capacity

B. fill:插入資料

用put() 方法可以插入一段資料 但是這個時候postion 在插入這段資料的尾部

C. flip:彈出資料

因為上面寫入資料後,指針在尾部不是從0開始,那麼我們需要

buffer.limit(buffer.position( )).position(0);

将limit 設定為目前的postion,limit 可以告訴方法這段資料什麼時候結束,而具體可以調用方法

hasRemaining( ) 來判斷是否資料結束。

同時将目前指針設定為0

NIO 提供了方法buffer.flip()完成上面的動作

另外方法rewind( )和flip相似 隻是差別在于flip要修改limit,而rewind不修改limit

rewind 用處可以在已經flip後,還能重讀這段資料

Buffer不是線程安全的,如果你想要多線程同時通路,那麼需要用synchronization

D. Mark:讓buffer記住某個指針,已備後面之用。

調用mark()就能将目前指針mark住

而調用reset()就能将目前指針回到mark了的指針位置,如果mark未定義,那麼調用reset就會報InvalidMarkException

此外rewind( ), clear( ), and flip( )這些操作将會抛棄mark,這樣clear()和reset()差別

clear表示清空buffer 而reset表示能将目前指針回到mark了的指針位置

E:Comparing:buffer 也是java的object sub class,所有buffer也能對象進行比較

public abstract class ByteBuffer

extends Buffer implements Comparable

{

// This is a partial API listing

public boolean equals (Object ob)

public int compareTo (Object ob)

}

兩個buffer 認為相等,需要具備下面條件

1.buffer 類型相同

2.buffer裡面保留的元素數量相同,buffer capacities不需要相同,這裡需要注意保留的元素是指有效的元素,不是指buffer裡面有的元素。

其實就從position到limit這段的元素,當然目前的postion和limit值都可以不相同,但這段資料要相同

3.remaining data 的資料順序要相同,可以通過get()來循環擷取出來後判斷每個element都相同

如果同時符合上面三個條件,那麼就表示buffer object 相等

F:Bulk Moves

如何在buffer中大塊的資料移動,對性能起到關鍵作用

他的做法是get的時候,将一塊資料取出放入數組裡面,這樣比起循環get()一個byte要效率高多了,那麼對于塊狀資料總是有個指定的長度

這個長度就是指定數組的長度

public CharBuffer get (char [] dst, int offset, int length)

這裡就是length。

如果隻有參數為數組buffer.get (myArray);

那麼其實也是指定了長度,隻是長度為數組的長度,上面的語句等同于

buffer.get (myArray, 0, myArray.length);

針對上面,有可能buffer的data 小于myArray.length,這個時候如果取buffer 那麼會報錯

是以需要下面的寫法

 char [] bigArray = new char [1000];

 // Get count of chars remaining in the buffer

 int length = buffer.remaining( );

 // Buffer is known to contain < 1,000 chars

 buffer.get (bigArrray, 0, length);

 // Do something useful with the data

 processData (bigArray, length);

 這是buffer<length

 如果buffer>length 那需要loop:

 char [] smallArray = new char [10];

 while (buffer.hasRemaining( )) {

 int length = Math.min (buffer.remaining( ), smallArray.length);

 buffer.get (smallArray, 0, length);

 processData (smallArray, length);

 }

同樣操作put是将array中的資料放入buffer,他同樣有上面length的問題,需要用remaining來判斷

G:建立Buffer: buffer class 不能直接new建立,他們都是abstract class,但是他們都有static factory,通過factory可以建立instances

比如

public abstract class CharBuffer

extends Buffer implements CharSequence, Comparable

public static CharBuffer allocate (int capacity)

public static CharBuffer wrap (char [] array)

public static CharBuffer wrap (char [] array, int offset,

int length)

public final boolean hasArray( )

public final char [] array( )

public final int arrayOffset( )

可以通過allocation或者wrapping來建立buffer

Alloction:允許配置設定私有buffer

CharBuffer charBuffer = CharBuffer.allocate (100);

wrapping:不允許配置設定私有buffer,顯性提供存儲

char [] myArray = new char [100];

CharBuffer charbuffer = CharBuffer.wrap (myArray);

而myArray  叫做buffer的backing array

通過hasArray( ) 判斷buffer是否存在backing array

如果為true,可通過array()得到array的引用

H:Duplicating Buffers  複制buffer

Duplicat() 建立一個和原來一樣的新的buffer,他們都有各自的postion,limit,mark, 但是共享同樣的資料,也就是說任何一個buffer被修改了

他們看到的資料也就修改了。建立的新buffer繼承了老buffer的屬性,比如如果老buffer為readonly,那麼新的也是readonly

asReadOnlyBuffer( ) 和Duplicat()相似,隻是差別asReadOnlyBuffer( ) 建立的buffer總是readonly

slice() 和Duplicat()也相似,不同之處在于slice() 将修改buffer的capacity=(limit - position),同時新buffer以老buffer的目前postion作為開始

指針。

I:Byte Buffers 可以擷取不同類型的buffer,另外byte本身也能排序

J:Direct Buffers: 對于作業系統來說,記憶體存儲的資料不一定是連續的。而Direct buffer直接用于和io裝置進行互動,對于io裝置操作的buffer隻能是

direct buffer,如果是nondirect ByteBuffer需要寫入裝置,那麼他首先是建立一個臨時的direct byteBuffer,然後将内容考入這個臨時direct buffer,

接着進行底層的io操作,完成io操作後,臨時buffer将被垃圾回收。

ByteBuffer.allocateDirect()建立direct buffer,isDirect( )則判斷buffer是否是direct buffer

K:View Buffers  當一堆資料被收到後需要先檢視他,然後才能确定是send還是怎麼處理,這個時候就要引入View buffer

View Buffer擁有自己的屬性,比如postion,limit,mark,但是他是和初始buffer共享資料的,這個和duplicated,sliced相似,但是view Buffer

能将raw bytes 映射為指定的基礎類型buffer,這個也是檢視的具體内容了,我們也可以認為是byte buffer向其他基礎類型buffer的轉換

public abstract CharBuffer asCharBuffer( );

public abstract ShortBuffer asShortBuffer( );

public abstract IntBuffer asIntBuffer( );

public abstract LongBuffer asLongBuffer( );

public abstract FloatBuffer asFloatBuffer( );

public abstract DoubleBuffer asDoubleBuffer( );

繼續閱讀