【從入門到放棄-Java】併發編程-NIO-Buffer

前言

上篇【從入門到放棄-Java】併發編程-NIO-Channel中咱們學習到channel是雙向通道,數據經過channel在實體(文件、socket)和緩衝區(buffer)中能夠雙向傳輸。html

本文咱們就來學習下buffer編程

簡介

buffer即緩衝區,其實是一塊內存,能夠用來寫入、讀取數據。是一個線性的、大小有限的、順序承載基礎數據類型的內存塊。數組

buffer有三個重要的屬性:緩存

  • capacity:緩衝池大小,是不可變的。當buffer寫滿時,須要先清空才能繼續寫入。
  • limit:是buffer中不能夠被讀或者寫的第一個元素的位置,limit的大小永遠不會超過capacity(在寫模式下,limit等於capacity)
  • position:是buffer中能夠被讀或者寫的第一個元素的位置,position的大小永遠不會超過limit

除了boolean外,每個基礎數據類型都有對應的buffer。如:ByteBuffer、CharBuffer、LongBuffer等安全

buffer不是線程安全的,若是要在多線程中使用 須要加鎖控制網絡

接下來以ByteBuffer爲例開始學習。多線程

ByteBuffer

allocateDirect

public static ByteBuffer allocateDirect(int capacity) {
    //會建立一個容量大小爲capacity的DirectByteBuffer(ByteBuffer的子類)
    return new DirectByteBuffer(capacity);
}

allocate

public static ByteBuffer allocate(int capacity) {
    if (capacity < 0)
        throw createCapacityException(capacity);
    //會建立一個容量大小爲capacity的HeapByteBuffer(ByteBuffer的子類)
    return new HeapByteBuffer(capacity, capacity);
}

HeapByteBuffer和DirectByteBuffer的區別:併發

  • DirectByteBuffer是直接調用native方法在本機os::malloc()建立堆外內存;HeapByteBuffer是直接在jvm的堆中分配內存。
  • 當buffer中的數據和磁盤、網絡等的交互都在操做系統的內核中發生時,使用DirectByteBuffer能避免從內核態->用戶態->內核態的切換開銷,全部的處理都在內核中進行,性能會比較好
  • 當頻繁建立操做數據量比較小的buffer時,使用HeapByteBuffer在jvm堆中分配內存能抵消掉使用DirectByteBuffer帶來的好處。

wrap

public static ByteBuffer wrap(byte[] array,
                                    int offset, int length)
{
    try {
        return new HeapByteBuffer(array, offset, length);
    } catch (IllegalArgumentException x) {
        throw new IndexOutOfBoundsException();
    }
}

public static ByteBuffer wrap(byte[] array) {
        return wrap(array, 0, array.length);
    }

將byte數組包裝成一個ByteBufferjvm

讀數據

  • 使用get方法從Buffer中讀取數據
  • 從Buffer中讀取數據到Channel即:Channel::write() (從buffer中讀取數據寫入到資源中,因此是write)

寫數據

  • 使用put方法直接設置Buffer中的數據
  • 從Channel中讀取數據到Buffer即:Channel::read() (從資源中讀取數據寫入到buffer中,因此是read)

position

//獲取buffer中當前position的位置
public final int position() {
    return position;
}

//設置buffer的position爲newPosition,注意newPosition要大於0且小於limit,若是remark大於newPosition則設置爲-1
public Buffer position(int newPosition) {
    if (newPosition > limit | newPosition < 0)
         throw createPositionException(newPosition);
     position = newPosition;
     if (mark > position) mark = -1;
     return this;
}

limit

//獲取buffer中當前limit的位置
public final int limit() {
    return limit;
}

//設置buffer的limit爲newLimit,注意newLimit要大於0且小於capacity。若是position大於newLimit這設置爲newLimit,若是remark大於newLimit則設置爲-1
public Buffer limit(int newLimit) {
    if (newLimit > capacity | newLimit < 0)
        throw createLimitException(newLimit);
    limit = newLimit;
    if (position > limit) position = limit;
    if (mark > limit) mark = -1;
    return this;
}

mark

public Buffer mark() {
    //標記mark爲當前position
    mark = position;
    return this;
}

將當前位置作標記,在使用reset方法時,能夠回到當前mark的位置socket

reset

public Buffer reset() {
    int m = mark;
    if (m < 0)
        throw new InvalidMarkException();
    //設置position爲當前mark
    position = m;
    return this;
}

回到以前設置mark的位置

clear

public Buffer clear() {
    //設置position爲0
    position = 0;
    //limit設置爲capacity大小
    limit = capacity;
    //mark設置爲-1(初始化)
    mark = -1;
    return this;
}

讀取完數據後調用clear,即將buffer邏輯上清空了,能夠從0開始寫入數據

flip

public Buffer flip() {
    //limit設置爲當前位置
    limit = position;
    //position設置爲0
    position = 0;
    //mark設置爲-1(初始化)
    mark = -1;
    return this;
}

將buffer從寫模式設置爲讀模式,limit設置爲當前position的位置,即只能讀取limit大小的數據

rewind

public Buffer rewind() {
    position = 0;
    mark = -1;
    return this;
}

將position設置爲0,即從頭開始讀取

remaining

public final int remaining() {
    return limit - position;
}

返回buffer中還有多少byte是未讀的

hasRemaining

public final boolean hasRemaining() {
    return position < limit;
}

是否已讀完

compact

public ByteBuffer compact() {
    System.arraycopy(hb, ix(position()), hb, ix(0), remaining());
    position(remaining());
    limit(capacity());
    discardMark();
    return this;
}

將position和limit直接的數據copy到byteBuffer的起始處,將已讀數據清空,並將新的position設置爲當前未讀數據的末尾。這樣能避免clear方法會將未讀數據也清空的問題

slice

public ByteBuffer slice() {
    return new HeapByteBufferR(hb,
                                    -1,
                                    0,
                                    this.remaining(),
                                    this.remaining(),
                                    this.position() + offset);
}


ByteBuffer slice(int pos, int lim) {
    assert (pos >= 0);
    assert (pos <= lim);
    int rem = lim - pos;
    return new HeapByteBufferR(hb,
                                    -1,
                                    0,
                                    rem,
                                    rem,
                                    pos + offset);
}

新建立一個ByteBuffer,將緩存區分片,設置一個子緩衝區,實際上內存仍是共享的,數據發生改變,兩個緩衝區讀取的數據都會是改變後的。

總結

Buffer最重要的三個屬性:position、limit、capacity。牢記這三個屬性的含義及讀寫切換時,設置值是如何變化的,Buffer的核心知識點就掌握了。


原文連接 本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索