NIO(2、Buffer)

目錄

NIO(1、概述)
NIO(2、Buffer)
NIO(3、Channel)
NIO(4、Selector)html

Buffer

前文講了NIO與IO的區別,那麼這一章開始講述NIO下核心類 - Buffer類java

  上一章就說過,NIO的核心包括三個部分:通道(Channel)、選擇器(Selector)、緩衝區(Buffer),儘管還有其它的部分,例如管道(Pipe)、文件鎖(FileLock)、字符集(Charset)或甚是java.nio包下的異常類,這些都是做爲工具而被使用。安全

  提及緩衝區,咱們都知道是臨時數據存儲的地方,官方給它的定義是:app

A container for data of a specific primitive type. 特定基礎類型數據的容器工具

  其實咱們看它的實現類就能看出來this

  • ByteBuffer
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

  幾乎每種基礎數據類型都會有一種Buffer的實現,它們的操做方式幾乎一致,都有讀(get)/寫(put)操做,並且都是抽象類,因此,基於這些基礎類型的緩衝區,還有更具體的實現,例如ByteBuffer類有HeapByteBuffer和MappedByteBuffer兩個子類。第一次閱讀代碼可能有些奇怪,既然全部基礎類型數據的緩衝區都從Buffer類繼承,那麼Buffer類中並無抽象出讀(get)/寫(put)方法,而是每一個子類中分別抽象自己類型的讀(get)/寫(put)操做。一樣分配緩衝區大小方法( allocate() )也是在子類中定義,Buffer類自己並沒對外提供任何初始化的方法,即使是構造也是爲了子類繼承使用,因而可知,在使用Buffer的時候,咱們應該明確知道該使用哪一類基礎類型的緩衝區。線程

Capacity、Limit、Position

  咱們閱讀Buffer類代碼會發現,Buffer類定義四個int類型的私有字段:mark、position、limit、capacity。其實瞭解完這四個字段,咱們就明白Buffer是如何工做的。code

不管如何,它們的大小都會遵守這個規則 :mark <= position <= limit <= capacityhtm

  這是Buffer讀/寫模式時position、limit、capacity的圖。對象

  咱們看一段簡單代碼:

//code 
IntBuffer intBuffer = IntBuffer.wrap(new int[]{1, 2, 3, 4, 5, 6, 7});
System.out.println("capacity -> " + intBuffer.capacity());
System.out.println("limit -> " + intBuffer.limit());
System.out.println("position -> " + intBuffer.position());
//切換讀模式
intBuffer.flip();
System.out.println("capacity -> " + intBuffer.capacity());
System.out.println("limit -> " + intBuffer.limit());
System.out.println("position -> " + intBuffer.position());
//console:
//寫模式
capacity -> 7
limit -> 7
position -> 0
//讀模式
capacity -> 7
limit -> 0
position -> 0

  初始化了一個IntBuffer對象,而後打印capacity、limit、position的值,清楚的能夠看到,capacity和limit初始值是初始化容量的大小,position爲0。當切換讀模式時,capacity的值一樣爲7,limit和position值爲0。
  接上一段代碼:

//code
intBuffer.clear();
intBuffer.put(100);
intBuffer.put(200);
System.out.println("position -> " + intBuffer.position());
//切換讀模式
intBuffer.flip();
int pVal0 = intBuffer.get(0);
System.out.println("capacity -> " + intBuffer.capacity());
System.out.println("limit -> " + intBuffer.limit());
System.out.println("position -> " + intBuffer.position());
//console
position -> 2
//讀模式
capacity -> 7
limit -> 2
position -> 0

  position就是你在往Buffer中寫數據時,標示當前的位置,初始化的position確定是0,當你寫入一個數據,position就會增長1。但咱們發現咱們讀操做並不會改變position的值,由於get操做只會檢查索引有無越界,而後取出數據,並不像put操做會把position增長1。
  另外,要說明一下的是,mark也是Buffer中的一個標記,當緩衝區初始化時、設置新的position或limit的值時、清理緩衝區時、設置讀模式時等,mark值都會被標爲-1。僅僅在使用mark()方法的時候,mark字段纔會被賦予position值。mark做爲position的一個臨時存儲的變量而存在,咱們隨時均可以調用reset()把以前存儲的原position值從新賦給position變量。當一個position不夠用時,咱們須要多一個臨時變量存儲,這彷佛就是mark存在的意義了。

  一樣,咱們也發現capactiy就是緩衝區的容量,緩衝區的容量在初始化以後就不會變,不管你執行clear()或是compact()方法,它們都不會改變緩衝區的容量,除非被回收整個緩衝區。當咱們初始化一個緩衝區以後,在明知道容量只有3的狀況下,卻硬是塞4個值,那麼運行會拋出java.nio.BufferOverflowException異常。
  接上一段代碼:

System.out.println("limit -> " + intBuffer.limit());
//code 1.1
intBuffer.get(5);
System.out.println("limit -> " + intBuffer.limit());
//code 1.2
intBuffer.limit(intBuffer.capacity());
intBuffer.get(5);
System.out.println("limit -> " + intBuffer.limit());

//console
limit -> 2
//code 1.1
java.lang.IndexOutOfBoundsException
//code 1.2
limit -> 7

  limit一樣標示使用的上限,表示你能寫多少數據,或你能讀多少數據的值。換句話說,就是讀/寫的時候,limit大小決定了你能讀/寫多少。上述代碼說明了這一點,進入代碼塊時limit的值爲2,意味着只能讀索引爲0或1的數據,只有當limit被從新賦值,你才能讀取新值範圍之內的數據。

線程安全

  Buffer類的實現,決定了這一定是線程不安全的,若是須要在多個線程中使用,咱們須要主動加上同步控制。

方法

  這裏只介紹幾個比較重要的操做方法。
  flip(),切換讀模式。

public final Buffer flip() {
        limit = position;
        position = 0;
        mark = -1;
        return this;
}

  rewind(),重讀,能夠理解成倒帶,即使已讀過的數據內容也可重讀一遍。

public final Buffer rewind() {
        position = 0;
        mark = -1;
        return this;
}

  clear(),清除緩衝區內容,能夠將新的數據從新寫進緩衝區.。

public final Buffer clear() {
        position = 0;
        limit = capacity;
        mark = -1;
        return this;
}
相關文章
相關標籤/搜索