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必須是整數; ...
一周排行
  • 本文將介紹如何在.NET Core3環境下使用MVVM框架Prism的使用區域管理器對於View的管理 一.區域管理器 我們在之前的Prism系列構建了一個標準式Prism項目,這篇文章將會講解之前項目中用到的利用區域管理器更好的對我們的View進行管理,同樣的我們來看看官方給出的模型圖: 現在我們 ...
  • 前言: gRPC預設是ProtoFirst的,即先寫 proto文件,再生成代碼,需要人工維護proto,生成的代碼也不友好,所以出現了gRPC CodeFirst,下麵來說說我們是怎麼實現gRPC CodeFirst 目錄: 實現和WCF一樣的CodeFirst (1). 實現gRPC CodeF ...
  • [toc] 1.背景 接上篇文章 "深入淺出C 結構體——封裝乙太網心跳包的結構為例" ,使用結構體性能不佳,而且也說明瞭原因。本篇文章詳細描述了以類來封裝網路心跳包的優缺點,結果大大提升瞭解析性能。 2.用類來封裝乙太網心跳包的優缺點 2.1.優點 + 可以在類里直接new byte[],即直接實 ...
  • VS2013如何轉成VS2010且不會出現此項目與Visual Studio的當前版本不相容的報錯 解決方法: 1.用記事本打開解決方案文件“解決方案名.sln”,然後修改最上面兩行為如下代碼:Microsoft Visual Studio Solution File, Format Version ...
  • 傳遞數據至部分視圖: 在ps.cshtml中get到上面高亮的參數: ...
  • 三、C#數據類型 3.1.變數 聲明->賦值->使用 作用域:變數作用域為包含它的大括弧內 3.2.常量 1)const 數據類型 常量名稱 = 常量值 聲明常量時一定要賦值 2)@作用 輸出轉義字元 @"Hello World\n" 讓字元串換行 關鍵字用作標識符 @namespace @clas ...
  • 四、C#表達式與運算符 4.1.表達式 操作數+運算符 4.2.數學運算符 var++ 先用後加 ++var 先加後用 4.3.賦值運算符 略 4.4.關係運算符 結果只會是bool類型 1)對象的不同 數值類型比較兩個數的大小 字元類比較Unicode編碼大小,'A'=65 'a'=97 '0'= ...
  • 下麵的靜態代碼中: 現在想把箭頭所指的值,改為動態。 根據不同條件,它將有可能是1,或是3或是2或是5等。 ...
  • 使用Xaml+C#,使WPF/UWP運行在Linux和Mac上 ...
  • 前言 C 的lambda和Linq可以說是一大亮點,C 的Lambda無處不在,Linq在數據查詢上也有著舉足輕重的地位。 那麼什麼是Linq呢,Linq是 (語言集成查詢)的縮寫,可以對本地對象 集合 或者遠程數據源進行結構化的查詢操作。 那什麼又是Lambda呢?嗯,簡單來講就是匿名函數,我們不 ...
x