6. 彤哥說netty系列之Java NIO核心組件之Buffer

来源:https://www.cnblogs.com/tong-yuan/archive/2019/12/03/11980067.html

——日拱一卒,不期而至! 你好,我是彤哥,本篇是netty系列的第六篇。 簡介 上一章我們一起學習了Java NIO的核心組件Channel,它可以看作是實體與實體之間的連接,而且需要與Buffer交互,這一章我們就來學習一下Buffer的特性。 概念 Buffer用於與Channel交互時使用,通 ...


——日拱一卒,不期而至!

nio

你好,我是彤哥,本篇是netty系列的第六篇。

簡介

上一章我們一起學習了Java NIO的核心組件Channel,它可以看作是實體與實體之間的連接,而且需要與Buffer交互,這一章我們就來學習一下Buffer的特性。

概念

Buffer用於與Channel交互時使用,通過上一章的學習我們知道,數據從Channel讀取到Buffer,或者從Buffer寫入Channel。

nio

Buffer本質上是一個記憶體塊,可以向裡面寫入數據,或者從裡面讀取數據,在Java中它被包裝成了Buffer對象,並提供了一系列的方法用於操作這個記憶體塊。

屬性

為了更好地理解Buffer的數據結構,我們必須熟悉它的三個常用屬性:

  • capacity:容量
  • position:當前位置
  • limit:限制長度

在讀模式和寫模式下,position和limit的位置有所不同,見下圖:

nio

capacity

Buffer作為一個存儲塊,是有固定大小的,這個固定大小我們稱作“容量”。

當Buffer寫滿之後,需要先清空或者讀取數據,才能繼續寫入新的數據。

position

寫模式下,position從0開始,每寫入一個單位的數據,position前進一位,position最大可到達(capacity-1)的位置。

當Buffer從寫模式切換為讀模式時,position將重置為0。讀取數據時,同樣地,position每讀取一個單位,前進一位,此時,position最大可到達limit的位置(實際最大可讀取的位置是(limit-1))。

limit

寫模式下,limit最大值等於capacity。

讀模式下,limit最大值等於切換為讀模式時position的值,本文來源工從號彤哥讀源碼。

這裡可能有點繞,position類似於數組的下標,是從0開始的,limit表示最大可以讀取或者寫入的長度,capacity表示最大的容量,limit和capacity不是下標,類似於數組的長度,所以跟position比較需要-1。在寫模式下,position指向的是下一個待寫入的位置;在讀模式下,position指向的是下一個待讀取的位置。

類型

Java NIO自帶的Buffer類型有:

  • ByteBuffer
  • MappedByteBuffer
  • CharBuffer
  • ShortBuffer
  • IntBuffer
  • LongBuffer
  • FloatBuffer
  • DoubleBuffer

與基本類型一樣,每一種Buffer的基本單位長度不一樣罷了。

其中,MappedByteBuffer是一種特殊的ByteBuffer,它使用記憶體映射的方式載入物理文件,並不會耗費同等大小的物理記憶體,是一種直接操作堆外記憶體的方式,讀寫性能比較高。

基本用法

上面我們學習了Buffer的數據結構以及常用的Buffer類型,它們怎麼使用呢?常見的用法主要有四種:

  • 將數據寫入Buffer
  • 切換為讀模式flip()
  • 從Buffer中讀取數據
  • 清空數據並切換為寫模式clear()或者compact()

來個慄子

nio

public class FileChannelTest {
    public static void main(String[] args) throws IOException {
        // 從文件獲取一個FileChannel
        FileChannel fileChannel = new RandomAccessFile("D:\\object.txt", "rw").getChannel();
        // 分配一個Byte類型的Buffer
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // 將FileChannel中的數據讀出到buffer中,-1表示讀取完畢
        // buffer預設為寫模式,本文來源工從號彤哥讀源碼
        // read()方法是相對channel而言的,相對buffer就是寫
        while ((fileChannel.read(buffer)) != -1) {
            // buffer切換為讀模式
            buffer.flip();
            // buffer中是否有未讀數據
            while (buffer.hasRemaining()) {
                // 讀取數據
                System.out.print((char)buffer.get());
            }
            // 清空buffer,為下一次寫入數據做準備
            // clear()會將buffer再次切換為寫模式
            buffer.clear();
        }
    }
}

allocate()

要獲取一個Buffer對象,必須先分配它,每個Buffer類都有一個allocate()方法用於分配Buffer對象。

以下示例分配了一個容量為1024的ByteBuffer對象:

ByteBuffer buffer = ByteBuffer.allocate(1024);

下麵是分配了一個容量為48的CharBuffer的對象:

CharBuffer buf = CharBuffer.allocate(48);

將數據寫入Buffer

將數據寫入Buffer有兩種形式:

  • 從Channel讀出數據並寫入Buffer,也叫從Channel讀入Buffer
  • 調用Buffer自己的put()方法寫入數據

從Channel讀入Buffer的示例如下:

int bytesRead = inChannel.read(buf); //讀入Buffer

Buffer自己put()寫入數據的示例如下:

buf.put(127);

當然,put()有很多不同的類型,比如在特定位置寫入,寫入不同類型的數據等等,可以在IDEA中按F12查看。

flip()

flip()方法用於將Buffer從寫模式切換為讀模式,position將切換到0位置,且limit將切換到剛纔position的位置。

也就是說,position變成了可讀數據的首位,limit表示可以讀取的最大數據長度。

從Buffer中讀取數據

從Buffer中讀取數據也有兩種形式:

  • 從Buffer讀取數據,並寫入Channel,也叫作從Buffer寫入Channel
  • 調用Buffer自己的get()方法讀取數據

從Buffer寫入Channel的示例如下:

// 本文來源工從號彤哥讀源碼
int bytesWritten = inChannel.write(buf);

調用Buffer自己的get()方法讀取數據的示例如下:

byte aByte = buf.get();   

當然,get()有很多不同的類型,比如從特定的位置讀取,讀取不同類型的數據等等,可以在IDEA中按F12查看。

rewind()

rewind()方法會重置position為0,但limit保持不變,因此可以用來重新讀取數據。通常是在重新讀取數據之前調用。

clear()

clear()方法用於清空整個Buffer,並將Buffer從讀模式切換回寫模式,且position歸位到0位置。

compact()

compact()方法用於清空已讀取的數據,並將未讀取的數據移至Buffer的頭部,position的位置移動到從頭開始計算的未讀取的數據的下一個位置,它也會將Buffer從讀模式切換回寫模式。

mark() 和 reset()

mark()方法用於標記給定位置,然後可以在之後通過reset()方法重新回到mark的位置,示例如下:

buffer.mark();

//多次調用buffer.get(),例如在解析過程中。

buffer.reset(); //將位置重新設置為標記。 

總結

今天我們學習了Java NIO核心組件Buffer,它經常跟Channel聯合起來使用。講到這裡我們一直在使用FileChannel在舉例,那麼它們到底跟網路編程有什麼關係呢?請聽下回分解。

參考

http://tutorials.jenkov.com/java-nio/channels.html

最後,也歡迎來我的工從號彤哥讀源碼系統地學習源碼&架構的知識。

nio


您的分享是我們最大的動力!

更多相關文章
  • 之前已經說過了SpringAOP中的幾種通知類型以及如何創建簡單的通知 "見地址" 一、什麼是切入點 通過之前的例子中,我們可以創建ProxyFactory的方式來創建通知,然後獲取目標類中的方法。通過不同類型的通知,能對這些方法做不同的事。但是,這種方式會對整個類中的所有方法都有作用,但是很多時間 ...
  • 命令模式,正如模式的名字一樣,該模式中的不同操作都可以當做不同的命令來執行,可以使用隊列來執行一系列的命令,也可以單獨執行某個命令。該模式重點是將不同的操作封裝為不同的命令對象,將操作的調用者與執行者進行解耦。 命令模式中的Command對象(即每一個命令,或者說命令對象)用於封裝在完成某項操作或觸 ...
  • pip install redis 五大數據類型使用 String類型 List類型 Hash類型 Set類型 Zset類型 全局key操作 ...
  • LinkedList實現原理(JDK1.8) LinkedList底層採用雙向鏈表,如果對鏈表這種結構比較熟悉的話,那LinkedList的實現原理看明白就相當容易。 鏈表通過“指針”將一組零散的記憶體塊串聯起來使用,每一個元素(節點)通過指針指向它的下一個元素,最後一個節點的下一個指向為null,而 ...
  • Java抽象類: 抽象類特點 :抽象類除了不能實例化對象之外,類的其它功能依然存在,成員變數、成員方法和構造方法的訪問方式和普通類一樣。 由於抽象類不能實例化對象,所以抽象類必須被extends [抽象類]方式 繼承才能被使用。 抽象類表示的是一種繼承關係。 (總結就是:抽象類裡面設計跟普通類一樣, ...
  • lua中json和table的互轉,是我們在平時開發過程中經常用到的。比如: 在用lua編寫的伺服器中,如果客戶端發送json格式的數據,那麼在lua處理業務邏輯的時候,必然需要轉換成lua自己的數據結構,如table。此時,就會用到table和json格式的互轉。 在用lua編寫的伺服器中,如果我 ...
  • 先定義消息類型 orders.proto 在GOPATH創建目錄和編譯這個消息類型輸出到該目錄,包名是message 編寫go文件進行序列化和反序列化剛纔生成的包里的類型結構體數據 ...
  • swap(a,b) 用於交換a,b兩個變數的值; max(a,b) 返回a,b中的最大值; min(a,b) 返回a,b中的最小值; abs(x) 返回x的絕對值,x必須是整數; ...
一周排行
  • HttpReports 簡單介紹 HttpReports 是 .Net Core下的一個Web組件,適用於 WebAPI 項目和 API 網關項目,通過中間件的形式集成到您的項目中, 通過HttpReports,可以讓開發人員快速的搭建出一個 API 性能分析的基礎報表網站。 主要包含 HttpRe ...
  • 大家好,這幾天試著從Github上拉取AspNetCore的源碼,嘗試著通過Visual Studio 打開,但是並不盡人意。我們需要去構建我們拉去的源代碼,這樣才可以通過VisualStudio可還原的項目。畢竟AspNetCore是一個巨型的項目集。 先決條件 在Windows中構建AspNet ...
  • 知識需要不斷積累、總結和沉澱,思考和寫作是成長的催化劑 梯子 一、鎖1、lock2、Interlocked3、Monitor4、SpinLock5、Mutex6、Semaphore7、Events1、AutoResetEvent2、ManualResetEvent3、ManualResetEvent ...
  • 安裝Docker CentOS 7 安裝 Docker 編寫Dockerfile 右鍵項目-》添加-》Docker 支持 選擇Linux 修改為如下: FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base WORKDIR ...
  • 原文:https://blogs.msdn.microsoft.com/mazhou/2017/06/06/c-7-series-part-3-default-literals/ C#的default關鍵字有兩種用法:一種是標記switch…case結構的預設分支(會匹配任意不被所有case條件匹配 ...
  • 相關模塊 1. AbpAspNetCoreModule 2. AbpAspNetCoreMvcModule 3. AbpAspNetCoreMvcContractsModule abp通過這三個模塊載入並配置了 asp.net core。,最主要的就是AbpAspNetCoreMvcModule模塊 ...
  • 安裝 參考文檔:https://docs.docker.com/install/linux/docker-ce/centos/#install-using-the-repository 前提條件 Docker 要求 CentOS 系統的內核版本高於 3.10,在終端輸入以下命令: uname -r ...
  • 目前遇到的問題: 1.路徑區分大小寫及路徑用“/”,而不是常用的"\\"。 windows下路徑為:"xxxx\\yyyy",Linux路徑下為:"xxxx/yyyy" 使用 Path.Combine("xxxx","yyyy") 進行合併即可。 2.有時候就需要在 docker 容器里訪問宿主機提 ...
  • 《.Net 最佳實踐》 [作者] (美) Stephen Ritchie[譯者] (中) 黃燈橋 黃浩宇 李永[出版] 機械工業出版社[版次] 2014年01月 第1版[印次] 2018年01月 第1次 印刷[定價] 69.00元 (P001) 開發人員應該對任何稱之為“最佳實踐”的實踐保持一種懷疑 ...
  • 本文介紹了C#中的屬性,以及C#6和C#7中與屬性相關的新特性。 ...
x