<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    【死磕 NIO】— 深入分析Buffer

    共 7245字,需瀏覽 15分鐘

     ·

    2021-11-16 02:26

    大家好,我是大明哥,今天我們來(lái)看看 Buffer。

    上面幾篇文章詳細(xì)介紹了 IO 相關(guān)的一些基本概念,如阻塞、非阻塞、同步、異步的區(qū)別,Reactor 模式、Proactor 模式。以下是這幾篇文章的鏈接,有興趣的同學(xué)可以閱讀下:

    從這篇文章開(kāi)始,我們將回歸 NIO 方面的相關(guān)知識(shí),首先從 NIO 的三大核心組件說(shuō)起。

    • Buffer

    • Channel

    • Selector

    首先是 Buffer

    Buffer

    Buffer 是一個(gè)抽象類(lèi),主要用作緩沖區(qū),其實(shí)質(zhì)我們可以認(rèn)為是一個(gè)可以寫(xiě)入數(shù)據(jù),然后從中讀取數(shù)據(jù)的內(nèi)存塊。這塊內(nèi)存被包裝成 NIO Buffer 對(duì)象,并提供一系列的方法便于我們?cè)L問(wèn)這塊內(nèi)存。

    要理解 Buffer 的工作原理,首先就要理解它的 4 個(gè)索引:

    • capacity:容量

    • position:位置

    • limit:界限

    • mark:標(biāo)記

    capacity 則表示該 Buffer 的容量,而 position 和 limit 的含義取決于 Buffer 處于什么模式(讀模式或者寫(xiě)模式),下圖描述讀寫(xiě)模式下這三種屬性的含義

    • capacity

    capacity 表示容量,Buffer 是一個(gè)內(nèi)存塊,其存儲(chǔ)數(shù)據(jù)的最大大小就是 capacity。我們不斷地往 Buffer 中寫(xiě)入數(shù)據(jù),當(dāng) Buffer 被寫(xiě)滿(mǎn)后也就是存儲(chǔ)的數(shù)據(jù)達(dá)到 capacity 了就需要將其清空,才能繼續(xù)寫(xiě)入數(shù)據(jù)。

    • position

    position 的含義取決于 Buffer 處于寫(xiě)模式還是讀模式:

    • 如果是寫(xiě)模式,則寫(xiě)入的地方就是所謂的 position,其初始值是 0,最大值是 capacity - 1,當(dāng)往 Buffer 中寫(xiě)入一個(gè)數(shù)據(jù)時(shí),position 就會(huì)向前移動(dòng)到下一個(gè)待寫(xiě)入的位置。

    • 如果是讀模式,則讀取數(shù)據(jù)的地方就是 position。當(dāng)執(zhí)行 flip() 將 buffer 從寫(xiě)模式切換到讀模式時(shí),position 會(huì)被重置為 0,隨著數(shù)據(jù)不斷的讀取,position 不斷地向前移,直到 limit。

    • limit

    與 position 一樣,limit 的含義也取決于 Buffer 處于何種模式:

    • 寫(xiě)模式:當(dāng) Buffer 處于寫(xiě)模式時(shí),limit 是指能夠往 Buffer 中寫(xiě)入多少數(shù)據(jù),其值等于 capacity

    • 讀模式:當(dāng) Buffer 處于讀模式時(shí),limit 表示能夠從 Buffer 中最多能夠讀取多少數(shù)據(jù)出來(lái),所以當(dāng) Buffer 從寫(xiě)模式切換到讀模式時(shí),limit 會(huì)被設(shè)置寫(xiě)模式下的 position 的值

    • mark

    mark 僅僅只是一個(gè)標(biāo)識(shí),可以通過(guò) mark() 方法進(jìn)行設(shè)置,設(shè)置值為當(dāng)前的 position

    Buffer 方法

    Buffer 提供了一系列的方法用來(lái)操作它,比如 clear() 用來(lái)清空緩沖區(qū),filp() 用來(lái)讀切換等等方法,下面將依次演示 Buffer 的主要方法,包含從 Buffer 獲取實(shí)例、寫(xiě)入數(shù)據(jù)、讀取數(shù)據(jù)、重置等等一個(gè)系列的操作流程,同時(shí)將 position、limit 兩個(gè)參數(shù)打印出來(lái),便于我們更好地理解 Buffer。

    allocate()

    要獲取一個(gè) Buffer 對(duì)象,首先就要為期分配內(nèi)存空間,使用 allocate() 方法分配內(nèi)存空間,如下:

    DoubleBuffer buffer = DoubleBuffer.allocate(10);

    System.out.println("================= allocate 10 后 =================");
    System.out.println("capacity = " + buffer.capacity());
    System.out.println("position = " + buffer.position());
    System.out.println("limit = " + buffer.limit());

    這里分配了 10 * sikeof(double) 字節(jié)的內(nèi)存空間。需要注意的是 allocate() 里面參數(shù)并不是字節(jié)數(shù),而是寫(xiě)入對(duì)象的數(shù)量,比如上面實(shí)例參數(shù)是 10 ,表明我們可以寫(xiě) 10 個(gè) double 對(duì)象。

    結(jié)果如下:

    ================= allocate 10 后 =================
    capacity = 10
    position = 0
    limit = 10

    此時(shí),Buffer 的情況如下:

    put()

    調(diào)用 allocate() 分配內(nèi)存后,得到 DoubleBuffer 實(shí)例對(duì)象,該對(duì)象目前處于寫(xiě)模式,我們可以通過(guò) put() 方法向 Buffer 里面寫(xiě)入數(shù)據(jù)。

    buffer.put(1);
    buffer.put(2);

    System.out.println("================= put 1、2 后 =================");
    System.out.println("capacity = " + buffer.capacity());
    System.out.println("position = " + buffer.position());
    System.out.println("limit = " + buffer.limit());

    調(diào)用 put() 往 DoubleBuffer 里面存放 2 個(gè)元素,此時(shí),各自參數(shù)值如下:

    ================= put 1、2 后 =================
    capacity = 10
    position = 2
    limit = 10

    我們看到 position 的值變成了 2 ,指向第三個(gè)可以寫(xiě)入元素的位置。這個(gè)時(shí)候我們?cè)賹?xiě)入 3 個(gè)元素:

    buffer.put(3);
    buffer.put(4);
    buffer.put(5);

    System.out.println("================= put 3、4、5 后 =================");
    System.out.println("capacity = " + buffer.capacity());
    System.out.println("position = " + buffer.position());
    System.out.println("limit = " + buffer.limit());

    得到結(jié)果如下:

    ================= put 3、4、5 后 =================
    capacity = 10
    position = 5
    limit = 10

    此時(shí),position 的值變成 5 ,指向第 6 個(gè)可以寫(xiě)入元素的位置。

    該 Buffer 的情況如下:

    flip()

    調(diào)用 put() 方法向 Buffer 中存儲(chǔ)數(shù)據(jù)后,這時(shí) Buffer 仍然處于寫(xiě)模式狀態(tài),在寫(xiě)模式狀態(tài)下我們是不能直接從 Buffer 中讀取數(shù)據(jù)的,需要調(diào)用 flip() 方法將 Buffer 從寫(xiě)模式切換為讀模式。

    buffer.flip();
    System.out.println("================= flip 后 =================");
    System.out.println("capacity = " + buffer.capacity());
    System.out.println("position = " + buffer.position());
    System.out.println("limit = " + buffer.limit());

    得到的結(jié)果如下:

    ================= flip 后 =================
    capacity = 10
    position = 0
    limit = 5

    調(diào)用 flip() 方法將 Buffer 從寫(xiě)模式切換為讀模式后,Buffer 的參數(shù)發(fā)生了微秒的變化:position = 0,limit = 5。前面說(shuō)過(guò)在讀模式下,limit 代表是 Buffer 的可讀長(zhǎng)度,它等于寫(xiě)模式下的 position,而 position 則是讀的位置。

    flip() 方法主要是將 Buffer 從寫(xiě)模式切換為讀模式,其調(diào)整的規(guī)則如下:

    • 設(shè)置可讀的長(zhǎng)度 limit。將寫(xiě)模式寫(xiě)的 Buffer 中內(nèi)容的最后位置 position 值變成讀模式下的 limit 位置值,新的 limit 值作為讀越界位置

    • 設(shè)置讀的起始位置。將 position 的值設(shè)置為 0 ,表示從 0 位置處開(kāi)始讀

    • 如果之前有 mark 保存的標(biāo)記位置,也需要消除,因?yàn)槟鞘菍?xiě)模式下的 mark 標(biāo)記

    調(diào)動(dòng) flip() 后,該 Buffer 情況如下:

    get()

    調(diào)用 flip() 將 Buffer 切換為讀模式后,就可以調(diào)用 get() 方法讀取 Buffer 中的數(shù)據(jù)了,get() 讀取數(shù)據(jù)很簡(jiǎn)單,每次從 position 的位置讀取一個(gè)數(shù)據(jù),并且將 position 向前移動(dòng) 1 位。如下:

    System.out.println("讀取第 1 個(gè)位置的數(shù)據(jù):" + buffer.get());
    System.out.println("讀取第 2 個(gè)位置的數(shù)據(jù):" + buffer.get());
    System.out.println("================= get 2 后 =================");
    System.out.println("capacity = " + buffer.capacity());
    System.out.println("position = " + buffer.position());
    System.out.println("limit = " + buffer.limit());

    連續(xù)調(diào)用 2 次 get() 方法,輸出結(jié)果:

    讀取第 1 個(gè)位置的數(shù)據(jù):1.0
    讀取第 2 個(gè)位置的數(shù)據(jù):2.0
    ================= get 2 后 =================
    capacity = 10
    position = 2
    limit = 5

    position 的值變成了 2 ,表明它向前移動(dòng)了 2 位,此時(shí),Buffer 如下:

    我們知道 limit 表明當(dāng)前 Buffer 最大可讀位置,buffer 也是一邊讀,position 位置一邊往前移動(dòng),那如果越界讀取呢?

    System.out.println("讀取第 3 個(gè)位置的數(shù)據(jù):" + buffer.get());
    System.out.println("讀取第 4 個(gè)位置的數(shù)據(jù):" + buffer.get());
    System.out.println("讀取第 5 個(gè)位置的數(shù)據(jù):" + buffer.get());
    System.out.println("讀取第 6 個(gè)位置的數(shù)據(jù):" + buffer.get());
    System.out.println("讀取第 7 個(gè)位置的數(shù)據(jù):" + buffer.get());

    limit = 5,6 、7 位置明顯越界了,如果越界讀取,Buffer 會(huì)拋出 BufferUnderflowException,如下:

    讀取第 3 個(gè)位置的數(shù)據(jù):3.0
    讀取第 4 個(gè)位置的數(shù)據(jù):4.0
    讀取第 5 個(gè)位置的數(shù)據(jù):5.0
    Exception in thread "main" java.nio.BufferUnderflowException
    at java.nio.Buffer.nextGetIndex(Buffer.java:500)
    at java.nio.HeapDoubleBuffer.get(HeapDoubleBuffer.java:135)
    at com.chenssy.study.nio.BufferTest.main(BufferTest.java:48)

    rewind()

    position 是隨著讀取的進(jìn)度一直往前移動(dòng)的,那如果我想在讀取一遍數(shù)據(jù)呢?使用 rewind() 方法,可以進(jìn)行重復(fù)讀。rewind() 也叫做倒帶,就想播放磁帶一樣,倒回去重新讀。

    buffer.rewind();
    System.out.println("================= rewind 后 =================");
    System.out.println("capacity = " + buffer.capacity());
    System.out.println("position = " + buffer.position());
    System.out.println("limit = " + buffer.limit());

    運(yùn)行結(jié)果:

    ================= rewind 后 =================
    capacity = 10
    position = 0
    limit = 5

    可以看到,僅僅只是將 position 的值設(shè)置為了 0,limit 的值保持不變。

    clear() 和 compact()

    flip() 方法用于將 Buffer 從寫(xiě)模式切換到讀模式,那怎么將 Buffer 從讀模式切換至寫(xiě)模式呢?可以調(diào)用 clear()compact() 兩個(gè)方法。

    • clear()
    buffer.clear();

    System.out.println("================= clear 后 =================");
    System.out.println("capacity = " + buffer.capacity());
    System.out.println("position = " + buffer.position());
    System.out.println("limit = " + buffer.limit());

    運(yùn)行結(jié)果如下:

    ================= clear 后 =================
    capacity = 10
    position = 0
    limit = 10

    調(diào)用 clear() 后,我們發(fā)現(xiàn) position 的值變成了 0,limit 值變成了 10,也就是 Buffer 被清空了,回歸到最初始狀態(tài)。但是里面的數(shù)據(jù)仍然是存在的,只是沒(méi)有標(biāo)記哪些數(shù)據(jù)是已讀,哪些為未讀。

    • compact()

    compact() 方法也可以將 Buffer 從讀模式切換到寫(xiě)模式,它跟 clear() 有一些區(qū)別。

    buffer.compact();

    System.out.println("================= compact 后 =================");
    System.out.println("capacity = " + buffer.capacity());
    System.out.println("position = " + buffer.position());
    System.out.println("limit = " + buffer.limit());

    運(yùn)行結(jié)果如下:

    ================= compact 后 =================
    capacity = 10
    position = 3
    limit = 10

    可以看到 position 的值為 3,它與 clear() 區(qū)別就在于,它會(huì)將所有未讀的數(shù)據(jù)全部復(fù)制到 Buffer 的前面(5次put(),兩次 get()),將 position 設(shè)置到這些數(shù)據(jù)后面,所以此時(shí)是從未讀的數(shù)據(jù)后面開(kāi)始寫(xiě)入新的數(shù)據(jù),Buffer 情況如下:

    mark() 和 reset()

    調(diào)用 mark() 方法可以標(biāo)志一個(gè)指定的位置(即設(shè)置 mark 的值),之后調(diào)用 reset() 時(shí),position 又會(huì)回到之前標(biāo)記的位置。

    通過(guò)上面的步驟演示,我想小伙伴基本上已經(jīng)掌握了 Buffer 的使用方法,這里簡(jiǎn)要總結(jié)下,使用 Buffer 的步驟如下:

    1. 將數(shù)據(jù)寫(xiě)入 Buffer 中

    2. 調(diào)用 flip() 方法,將 Buffer 切換為讀模式

    3. 從 Buffer 中讀取數(shù)據(jù)

    4. 調(diào)用 clear() 或者 compact() 方法將 Buffer 切換為寫(xiě)模式

    Buffer 的類(lèi)型

    在 NIO 中主要有 8 中 Buffer,分別如下:

    • ByteBuffer

    • CharBuffer

    • DoubleBuffer

    • FloatBuffer

    • IntBuffer

    • LongBuffer

    • ShortBuffer

    • MappedByteBuffer

    其 UML 類(lèi)圖如下:

    這些不同的 Buffer 類(lèi)型代表了不同的數(shù)據(jù)類(lèi)型,使得可以通過(guò) Buffer 直接操作如 char、short 等類(lèi)型的數(shù)據(jù)而不是字節(jié)數(shù)據(jù)。這些 Buffer 基本上覆蓋了所有能從 IO 中傳輸?shù)?Java 基本數(shù)據(jù)類(lèi)型,其中 MappedByteBuffer 是專(zhuān)門(mén)用于內(nèi)存映射的的一種 ByteBuffer,后續(xù)會(huì)專(zhuān)門(mén)介紹。

    到這里 Buffer 也就介紹完畢了,下篇文章將介紹它的協(xié)作者 Channel。


    瀏覽 74
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評(píng)論
    圖片
    表情
    推薦
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    日韩人妻丰满无码区A片 | 06久久人人 | 黄色99| 一级黄色在线观看 | 欧美人操B视频免费观看 |