中文字幕在线观看,亚洲а∨天堂久久精品9966,亚洲成a人片在线观看你懂的,亚洲av成人片无码网站,亚洲国产精品无码久久久五月天

JDK源碼閱讀:ByteBuffer

2018-08-10    來源:importnew

容器云強(qiáng)勢(shì)上線!快速搭建集群,上萬Linux鏡像隨意使用

Buffer是Java NIO中對(duì)于緩沖區(qū)的封裝。在Java BIO中,所有的讀寫API,都是直接使用byte數(shù)組作為緩沖區(qū)的,簡(jiǎn)單直接。但是在Java NIO中,緩沖區(qū)這一概念變得復(fù)雜,可能是對(duì)應(yīng)Java堆中的一塊內(nèi)存,也可能是對(duì)應(yīng)本地內(nèi)存中的一塊內(nèi)存。而byte數(shù)組只能用來指定Java堆中的一塊內(nèi)存,所以Java NIO中設(shè)計(jì)了一個(gè)新的緩沖區(qū)抽象,涵蓋了不同類型緩沖區(qū),這個(gè)抽象就是Buffer。

Buffer

Buffer是Java NIO中對(duì)于緩沖區(qū)的抽象。是一個(gè)用于存儲(chǔ)特定基本數(shù)據(jù)類型的容器。Buffer是特定基本數(shù)據(jù)類型的線性有限序列。

Java有8中基本類型:byte,short,int,long,float,double,char,boolean,除了boolean類型外,其他的類型都有對(duì)應(yīng)的Buffer具體實(shí)現(xiàn):

Buffer抽象類定義了所有類型的Buffer都有的屬性和操作,屬性如下:

  • capacity:緩沖區(qū)的容量,在緩沖區(qū)建立后就不能改變
  • limit:表示第一個(gè)不能讀寫的元素位置,limit不會(huì)大于capacity
  • position:表示下一個(gè)要讀寫的元素位置,position不會(huì)大于limit
  • mark:用于暫存一個(gè)元素位置,和書簽一樣,用于后續(xù)操作

所有的Buffer操作都圍繞這些屬性進(jìn)行。這些屬性滿足一個(gè)不變式:0<=mark<=position<=limit<=capacity

新建的Buffer這些屬性的取值為:

  • position=0
  • limit=capacity=用戶設(shè)置的容量
  • mark=-1

直接看定義比較抽象,可以看一下示意圖,下圖是一個(gè)容量為10的Buffer:

ByteBuffer的具體實(shí)現(xiàn)

所有Buffer實(shí)現(xiàn)中,最重要的實(shí)現(xiàn)是ByteBuffer,因?yàn)椴僮飨到y(tǒng)中所有的IO操作都是對(duì)字節(jié)的操作。當(dāng)我們需要從字節(jié)緩沖區(qū)中讀取別的數(shù)據(jù)類型才需要使用其他具體類型的Buffer實(shí)現(xiàn)。

ByteBuffer也是一個(gè)抽象類,具體的實(shí)現(xiàn)有HeapByteBuffer和DirectByteBuffer。分別對(duì)應(yīng)Java堆緩沖區(qū)與堆外內(nèi)存緩沖區(qū)。Java堆緩沖區(qū)本質(zhì)上就是byte數(shù)組,所以實(shí)現(xiàn)會(huì)比較簡(jiǎn)單。而堆外內(nèi)存涉及到JNI代碼實(shí)現(xiàn),較為復(fù)雜,本次我們以HeapByteBuffer為例來分析Buffer的相關(guān)操作,后續(xù)專門分析DirectByteBuffer。

ByteBuffer的類圖如下:

讀寫B(tài)uffer

Buffer作為緩沖區(qū),最主要的作用是用于傳遞數(shù)據(jù)。Buffer提供了一系列的讀取與寫入操作。因?yàn)椴煌愋偷腂uffer讀寫的類型不同,所以具體的方法定義是定義在Buffer實(shí)現(xiàn)類中的。與讀寫相關(guān)的API如下:

byte get()
byte get(int index)
ByteBuffer get(byte[] dst, int offset, int length)
ByteBuffer get(byte[] dst)

ByteBuffer put(byte b)
ByteBuffer put(int index, byte b)
ByteBuffer put(ByteBuffer src) 
ByteBuffer put(byte[] src, int offset, int length)

Buffer的讀寫操作可以按照兩種維度分類:

  • 單個(gè)/批量:
    • 單個(gè):一次讀寫一個(gè)字節(jié)
    • 批量:一次讀寫多個(gè)字節(jié)
  • 相對(duì)/絕對(duì):
    • 相對(duì):從Buffer維護(hù)的position位置開始讀寫,讀寫時(shí)position會(huì)隨之變化
    • 絕對(duì):直接指定讀寫的位置。指定index的API就是絕對(duì)API

接著我們來看看這些函數(shù)在HeapByteBuffer中是如何實(shí)現(xiàn)的:

final byte[] hb;    // 作為緩沖區(qū)的byte數(shù)組              
final int offset;   // 指定緩沖區(qū)的起始位置

public byte get() {
    // get操作就是直接從數(shù)組中獲取數(shù)據(jù)
    return hb[ix(nextGetIndex())];
}

public byte get(int i) {
    // 從指定位置獲取數(shù)據(jù),是絕對(duì)操作,只需檢查下標(biāo)是否合法
    return hb[ix(checkIndex(i))];
}

// 獲取下一個(gè)要讀取的元素的下標(biāo)
// position的定義就是下一個(gè)要讀寫的元素位置,
// 所以這里是返回position的當(dāng)前值,然后再對(duì)position進(jìn)行加一操作
final int nextGetIndex() {                          // package-private
    if (position >= limit)
        throw new BufferUnderflowException();
    return position++;
}

// 因?yàn)橹С制屏浚运愠鰜淼南聵?biāo)還需要加上偏移量
protected int ix(int i) {
    return i + offset;
}

單字節(jié)put與get邏輯一樣?匆幌屡縢et是如何實(shí)現(xiàn)的:

public ByteBuffer get(byte[] dst) {
    return get(dst, 0, dst.length);
}

public ByteBuffer get(byte[] dst, int offset, int length) {
    // 檢查參數(shù)是否越界
    checkBounds(offset, length, dst.length);
    // 檢查要獲取的長(zhǎng)度是否大于Buffer中剩余的數(shù)據(jù)長(zhǎng)度
    if (length > remaining())
        throw new BufferUnderflowException();
    // 調(diào)用System.arraycopy進(jìn)行數(shù)組內(nèi)容拷貝
    System.arraycopy(hb, ix(position()), dst, offset, length);
    // 更新position
    position(position() + length);
    return this;
}

可以看出,HeapByteBuffer是封裝了對(duì)byte數(shù)組的簡(jiǎn)單操作。對(duì)緩沖區(qū)的寫入和讀取本質(zhì)上是對(duì)數(shù)組的寫入和讀取。使用HeapByteBuffer的好處是我們不用做各種參數(shù)校驗(yàn),也不需要另外維護(hù)數(shù)組當(dāng)前讀寫位置的變量了。

同時(shí)我們可以看到,Buffer中對(duì)于position的操作沒有使用鎖進(jìn)行保護(hù),所以Buffer不是線程安全的。

Buffer的模式

雖然JDK的Java Doc并沒有提到Buffer有模式,但是Buffer提供了flip等操作用于切換Buffer的工作模式。在正確使用Buffer時(shí),一定要注意Buffer的當(dāng)前工作模式。否則會(huì)導(dǎo)致數(shù)據(jù)讀寫不符合你的預(yù)期。

Buffer有兩種工作模式,一種是接收數(shù)據(jù)模式,一種是輸出數(shù)據(jù)模式。

新建的Buffer處于接收數(shù)據(jù)的模式,可以向Buffer放入數(shù)據(jù),放入一個(gè)對(duì)應(yīng)基本類型的數(shù)據(jù)后,position加一,如果position已經(jīng)等于limit了還進(jìn)行put操作,則會(huì)拋出BufferOverflowException異常。

這種模式的Buffer可以用于Channel的read操作緩沖區(qū),或者是用于相對(duì)put操作。

比如向一個(gè)接受數(shù)據(jù)模式的Buffer put5個(gè)byte后的示例圖:

因?yàn)锽uffer的設(shè)計(jì)是讀寫的位置變量都使用position這個(gè)變量,所以如果要從Buffer中讀取數(shù)據(jù),要切換Buffer到輸出數(shù)據(jù)模式。Buffer提供了flip方法用于這種切換。

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

切換后的效果圖:

然后就可以從Buffer中讀取數(shù)據(jù)了。每次讀取一個(gè)元素,position就會(huì)加一,如果position已經(jīng)等于limit還進(jìn)行讀取,會(huì)拋出BufferUnderflowException異常。

可以看出Buffer本身沒有一個(gè)用于存儲(chǔ)模式的變量,模式的切換只是position和limit的變換而已。

flip方法只會(huì)把Buffer從接收模式切換到輸出模式,如果要從輸出模式切換到接收模式,可以使用compact或者clear方法,如果數(shù)據(jù)已經(jīng)讀取完畢或者數(shù)據(jù)不要了,使用clear方法,如果已讀的數(shù)據(jù)需要保留,同時(shí)需要切換到接收數(shù)據(jù)模式,使用compat方法。

// 壓縮Buffer,去掉已經(jīng)被讀取的數(shù)據(jù)
// 壓縮后的Buffer處于接收數(shù)據(jù)模式
public ByteBuffer compact() {
    System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
    position(remaining());
    limit(capacity());
    discardMark();
    return this;
}

// 清空Buffer,去掉所有數(shù)據(jù)(沒有做清理工作,是指修改位置變量)
// 清空后的Buffer處于接收數(shù)據(jù)模式
public final Buffer clear() {
    position = 0;
    limit = capacity;
    mark = -1;
    return this;
}

總結(jié)

  • Buffer是Java NIO對(duì)緩沖區(qū)的抽象
  • 除了boolean類型,其他的基本類型都有對(duì)應(yīng)的Buffer實(shí)現(xiàn)
  • 最常用的Buffer實(shí)現(xiàn)是ByteBuffer,具體的實(shí)現(xiàn)有HeapByteBuffer和DirectByteBuffer,分別對(duì)應(yīng)Java堆緩沖區(qū)與對(duì)外內(nèi)存緩沖區(qū)
  • HeapByteBuffer是對(duì)byte數(shù)組的封裝,方便使用
  • Buffer不是線程安全的
  • Buffer有兩種模式一種是接收數(shù)據(jù)模式,一種是輸出數(shù)據(jù)模式。新建的Buffer處于接收數(shù)據(jù)模式,使用flip方法可以切換Buffer到輸出數(shù)據(jù)模式。使用compact或者clear方法可以切換到接收數(shù)據(jù)模式。

參考資料

  • 堆外內(nèi)存 之 DirectByteBuffer 詳解 – 簡(jiǎn)書

標(biāo)簽: 安全 代碼

版權(quán)申明:本站文章部分自網(wǎng)絡(luò),如有侵權(quán),請(qǐng)聯(lián)系:west999com@outlook.com
特別注意:本站所有轉(zhuǎn)載文章言論不代表本站觀點(diǎn)!
本站所提供的圖片等素材,版權(quán)歸原作者所有,如需使用,請(qǐng)與原作者聯(lián)系。

上一篇:如何正確使用async/await?

下一篇:高效遍歷Java容器