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