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

JDK源碼閱讀:ByteBuffer

2018-08-10    來源:importnew

容器云強勢上線!快速搭建集群,上萬Linux鏡像隨意使用

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

Buffer

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

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

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

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

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

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

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

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

ByteBuffer的具體實現(xiàn)

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

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

ByteBuffer的類圖如下:

讀寫B(tài)uffer

Buffer作為緩沖區(qū),最主要的作用是用于傳遞數(shù)據(jù)。Buffer提供了一系列的讀取與寫入操作。因為不同類型的Buffer讀寫的類型不同,所以具體的方法定義是定義在Buffer實現(xià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的讀寫操作可以按照兩種維度分類:

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

接著我們來看看這些函數(shù)在HeapByteBuffer中是如何實現(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ù),是絕對操作,只需檢查下標是否合法
    return hb[ix(checkIndex(i))];
}

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

// 因為支持偏移量,所以算出來的下標還需要加上偏移量
protected int ix(int i) {
    return i + offset;
}

單字節(jié)put與get邏輯一樣?匆幌屡縢et是如何實現(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);
    // 檢查要獲取的長度是否大于Buffer中剩余的數(shù)據(jù)長度
    if (length > remaining())
        throw new BufferUnderflowException();
    // 調(diào)用System.arraycopy進行數(shù)組內(nèi)容拷貝
    System.arraycopy(hb, ix(position()), dst, offset, length);
    // 更新position
    position(position() + length);
    return this;
}

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

同時我們可以看到,Buffer中對于position的操作沒有使用鎖進行保護,所以Buffer不是線程安全的。

Buffer的模式

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

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

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

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

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

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

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

切換后的效果圖:

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

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

flip方法只會把Buffer從接收模式切換到輸出模式,如果要從輸出模式切換到接收模式,可以使用compact或者clear方法,如果數(shù)據(jù)已經(jīng)讀取完畢或者數(shù)據(jù)不要了,使用clear方法,如果已讀的數(shù)據(jù)需要保留,同時需要切換到接收數(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;
}

總結

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

參考資料

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

標簽: 安全 代碼

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

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

下一篇:高效遍歷Java容器