java nio之緩衝區(buffer)

1.java的nio採用的是多路複用模型。以下是對各個io模型的簡介。

-同步阻塞IO模型:首先,解釋一下阻塞與非阻塞。阻塞IO指的是需要內核IO操作徹底完成後才返回到用戶空間執行用戶程序的操作指令。“阻塞”指的是用戶程序(發起IO請求的進程或者線程)的執行狀態。可以說傳統的IO模型都是阻塞IO模型,並且在Java中默認創建的socket都屬於阻塞IO模型。其次,解釋一下同步與異步。簡單來說,可以將同步與異步看成發起IO請求的兩種方式。同步IO是指用戶空間(進程或者線程)是主動發起IO請求的一方,系統內核是被動接收方。異步IO則反過來,系統內核是主動發起IO請求的一方,用戶空間是被動接收方。同步阻塞IO(Blocking IO)指的是用戶空間(或者線程)主動發起,需要等待內核IO操作徹底完成後才返回到用戶空間的IO操作。在IO操作過程中,發起IO請求的用戶進程(或者線程)處於阻塞狀態。

-同步非阻塞模型:非阻塞IO(Non-Blocking IO,NIO)指的是用戶空間的程序不需要等待內核IO操作徹底完成,可以立即返回用戶空間去執行後續的指令,即發起IO請求的用戶進程(或者線程)處於非阻塞狀態,與此同時,內核會立即返回給用戶一個IO狀態值。阻塞和非阻塞的區別是什麼呢?阻塞是指用戶進程(或者線程)一直在等待,而不能做別的事情;非阻塞是指用戶進程(或者線程)獲得內核返回的狀態值就返回自己的空間,可以去做別的事情。在Java中,非阻塞IO的socket被設置爲NONBLOCK模式。

-IO多路複用模型:爲了提高性能,操作系統引入了一種新的系統調用,專門用於查詢IO文件描述符(含socket連接)的就緒狀態。在Linux系統中,新的系統調用爲select/epoll系統調用。通過該系統調用,一個用戶進程(或者線程)可以監視多個文件描述符,一旦某個描述符就緒(一般是內核緩衝區可讀/可寫),內核就能夠將文件描述符的就緒狀態返回給用戶進程(或者線程),用戶空間可以根據文件描述符的就緒狀態進行相應的IO系統調用。IO多路複用(IO Multiplexing)屬於一種經典的Reactor模式實現,有時也稱爲異步阻塞IO,Java中的Selector屬於這種模型。

-異步IO模型:異步IO(Asynchronous IO,AIO)指的是用戶空間的線程變成被動接收者,而內核空間成爲主動調用者。在異步IO模型中,當用戶線程收到通知時,數據已經被內核讀取完畢並放在了用戶緩衝區內,內核在IO完成後通知用戶線程直接使用即可。異步IO類似於Java中典型的回調模式,用戶進程(或者線程)向內核空間註冊了各種IO事件的回調函數,由內核去主動調用。

Buffer:屬於緩衝區,數據一般從通道中會讀入緩衝區,然後從緩衝區也可以寫入到通道。所以對於數據的讀寫緩衝區功不可沒,buffer有一系列的緩衝區子類。

capacity屬性Buffer類的capacity屬性表示內部容量的大小。一旦寫入的對象數量超過了capacity,緩衝區就滿了,不能再寫入了。Buffer類的capacity屬性一旦初始化,就不能再改變。原因是什麼呢?Buffer類的對象在初始化時會按照capacity分配內部數組的內存,在數組內存分配好之後,它的大小就不能改變了。前面講到,Buffer類是一個抽象類,Java不能直接用來新建對象。在具體使用的時候,必須使用Buffer的某個子類,例如DoubleBuffer子類,該子類能寫入的數據類型是double,如果在創建實例時其capacity是100,那麼我們最多可以寫入100個double類型的數據。

position屬性:Buffer類的position屬性表示當前的位置。position屬性的值與緩衝區的讀寫模式有關。在不同的模式下,position屬性值的含義是不同的,在緩衝區進行讀寫的模式改變時,position值會進行相應的調整。在寫模式下,position值的變化規則如下:

(1)在剛進入寫模式時,position值爲0,表示當前的寫入位置爲從頭開始。

(2)每當一個數據寫到緩衝區之後,position會向後移動到下一個可寫的位置。

(3)初始的position值爲0,最大可寫值爲limit-1。當position值達到limit時,緩衝區就已經無空間可寫了。

在讀模式下,position值的變化規則如下:

(1)當緩衝區剛開始進入讀模式時,position會被重置爲0。

(2)當從緩衝區讀取時,也是從position位置開始讀。讀取數據後,position向前移動到下一個可讀的位置。

(3)在讀模式下,limit表示可讀數據的上限。position的最大值爲最大可讀上限limit,當position達到limit時表明緩衝區已經無數據可讀。

Buffer的讀寫模式具體如何切換呢?

當新建了一個緩衝區實例時,緩衝區處於寫模式,這時是可以寫數據的。在數據寫入完成後,如果要從緩衝區讀取數據,就要進行模式的切換,可以調用flip()方法將緩衝區變成讀模式,flip爲翻轉的意思。在從寫模式到讀模式的翻轉過程中,position和limit屬性值會進行調整,具體的規則是:

(1)limit屬性被設置成寫模式時的position值,表示可以讀取的最大數據位置。

(2)position由原來的寫入位置變成新的可讀位置,也就是0,表示可以從頭開始讀。

 

limit屬性:Buffer類的limit屬性表示可以寫入或者讀取的數據最大上限,其屬性值的具體含義也與緩衝區的讀寫模式有關。在不同的模式下,limit值的含義是不同的,具體分爲以下兩種情況:

(1)在寫模式下,limit屬性值的含義爲可以寫入的數據最大上限。在剛進入寫模式時,limit的值會被設置成緩衝區的capacity值,表示可以一直將緩衝區的容量寫滿。

(2)在讀模式下,limit值的含義爲最多能從緩衝區讀取多少數據。一般來說,在進行緩衝區操作時是先寫入再讀取的。當緩衝區寫入完成後,就可以開始從Buffer讀取數據,調用flip()方法(翻轉),這時limit的值也會進行調整。具體如何調整呢?將寫模式下的position值設置成讀模式下的limit值,也就是說,將之前寫入的最大數量作爲可以讀取數據的上限值。Buffer在翻轉時的屬性值調整主要涉及position、limit兩個屬性,但是這種調整比較微妙,不是太好理解,下面舉一個簡單的例子:首先,創建緩衝區。新創建的緩衝區處於寫模式,其position值爲0,limit值爲最大容量capacity。

然後,向緩衝區寫數據。每寫入一個數據,position向後面移動一個位置,也就是position的值加1。這裏假定寫入了5個數,當寫入完成後,position的值爲5。最後,使用flip方法將緩衝區切換到讀模式。limit的值會先被設置成寫模式時的position值,所以新的limit值是5,表示可以讀取數據的最大上限是5。之後調整position值,新的position會被重置爲0,表示可以從0開始讀。緩衝區切換到讀模式後就可以從緩衝區讀取數據了,一直到緩衝區的數據讀取完畢。除了以上capacity、position、limit三個重要的成員屬性之外,Buffer還有一個比較重要的標記屬性:mark(標記)屬性。該屬性的大致作用爲:在緩衝區操作過程當中,可以將當前的position值臨時存入mark屬性中;需要的時候,再從mark中取出暫存的標記值,恢復到position屬性中,重新從position位置開始處理。

java.nio.Buffer類的子類有:

  • ByteBuffer
  • MappedByteBuffer(byte類型)
  • CharBuffer
  • DoubleBuffer
  • FloatBuffer
  • IntBuffer
  • LongBuffer
  • ShortBuffer

創建類:Buffer buf = CharBuffer.allocate(輸入緩衝區容量,一旦輸入就固定死了);//創建後默認是寫入模式。

子類方法:

allocate():表示創建一個緩衝對象。

put():表示往緩衝區寫入數據。

flip():翻轉表示將從寫入模式翻轉到讀取模式,注意:此處翻轉只是翻轉一下只能從寫入翻轉至讀取,並不能翻來翻去。

get():寫入模式下讀取數據,等讀取超過了緩衝區的數據上限時會拋出一個BufferUnderflowException異常。所以需要知道當前是不是到頭了。

capacity():緩衝區容量大小,寫入的時候注意別超過了,可調用此方法查看。

limit():緩衝區數據大小,讀取時可通過此方法查看可讀大小。

position():當前讀取的位置。以此搭配limit()可得知當前是否讀取到最大了。

rewind():倒帶。表示已經讀過一邊了,但想重新再讀一邊,調用此方法即可。

mark():臨時保存。再讀取過程中只要調用一下此方法,那麼此方法就會記錄當前位置。

reset():重置。調用此方法會將當前位置重置到mark()方法保存的位置。例如再緩衝區共有10個元素,當讀取到第5個元素時調用了mark(),那麼在將來某個時刻調用reset()方法就會將索引重置到mark方法保存的地方。

clear():表示從讀模式切換到寫模式。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章