天天看點

Java NIO源碼分析之Buffer

        Java NIO的主要讀寫處理邏輯就是将資料從通道讀入緩沖區,從緩沖區寫入到通道中。而這個資料緩沖區的基類就是Buffer。而Buffer本質上就是一塊可讀寫資料的記憶體,其提供了一些方法,友善外部調用者通路這塊記憶體進行資料讀寫操作。

        使用Buffer讀寫資料的主要步驟,大體如下:

        1、将資料寫入Buffer;

        2、調用flip()方法,将讀寫模式由寫模式切換成讀模式;

        3、從Buffer中讀取資料;

        4、調用clear()方法或者compact()方法清空緩沖區,完成資料讀操作。

        Buffer中三個十分重要的屬性

        1、capacity

        代表了Buffer的最大容量,辨別出Buffer中最多可以存儲的capacity個byte、long、char等類型的資料;

        2、position

        代表了緩沖區Buffer目前待寫入或待讀取位置,初始值為0,當一個byte、long、char等資料被寫入或者被讀取後,position自動移動到下一個可寫入位置,其最大值為capacity – 1,實際上它受limit限制。一般情況下,Buffer從寫模式切換到讀模式或者從讀模式切換到寫模式,position均會被重置為0;

        3、limit

        表示可寫入或可讀取資料的限制。在寫模式下,Buffer的limit表示你最多能往Buffer裡寫多少資料,limit等于Buffer的capacity。在讀模式下,limit表示你最多能讀到多少資料,當切換Buffer到讀模式時,limit會被設定成寫模式下的position值,也就是說你能讀到之前寫入的所有資料。

        Buffer中,擷取這些屬性的方法分别是:capacity()、position()、limit()方法,而position、limit也有對應方法進行設定,下面的源碼分析我們會分别介紹。

        Byffer中,一個不可變的定律是:mark <= position <= limit <= capacity。

        初始情況下:

        mark = -1

        position = 0

        limit = capacity - 1

        capacity = m 

        寫入n個資料時:

        mark = -1

        position = n

        capacity = m(m >= n)

        flip()切換寫模式至讀模式時:

        limit = position(也就是n)

        讀取k個資料時

        position = k

        Buffer有兩種模式:讀模式和寫模式。這兩種模式的切換是是通過以下方法完成的:

        1、寫模式到讀模式:flip()

        2、讀模式到寫模式:clear()或compact()(compact()方法為ByteBuffer中的抽象方法)

        Buffer源碼分析:

        flip()方法

        flip()實際上是翻轉資料緩沖區Buffer,将其由寫模式轉換成讀模式。大體邏輯如下:

        1、将limit設定為目前位置position,辨別出讀模式下最大可讀取資料限制為之前已寫入資料;

        2、目前位置position設定為0,辨別可讀取資料的起始位置;

        3、标記mark設定為-1,即無效;

        4、傳回目前Buffer執行個體。

        clear()方法

        clear()方法看上去像是清空緩沖區buffer,但是這個方法并不實際删除緩沖區中的資料,而是将其由讀模式轉換成寫模式。它的主要邏輯是:

        1、目前位置position設定為0,又可以從0開始寫入資料;

        2、讀寫限制limit設定為buffer的最大容量capacity,即我們可以寫入資料至完全填充整個緩沖區buffer;

        position()方法

        position()方法用于擷取目前緩沖區buffer的目前可寫或可讀位置position。

        position(int newPosition)方法

        rewind()方法

        一般情況下,flip隻能被調用一次,如果是資料需要被重新讀入,怎麼辦?這時rewind()方法就被派上用場了,其代碼如下: