【NIO】Buffer 详解

shadowLogo

简介:

一个用于存储 基本数据类型的容器
由 java.nio 包定义的,所有 缓冲区 都是 Buffer抽象类子类
Java NIO 中的 Buffer 主要用于 与 NIO 通道进行交互
数据 是从 通道(Channel) 读入 缓冲区(Buffer),从 缓冲区(Buffer) 写入 通道(Channel) 中的

Buffer 就像一个 数组,可以保存多个 相同类型的数据


继承关系:

继承关系

现在,本人来介绍下Buffer 抽象类的 常用子类

  • ByteBuffer —— 存储byte类型的缓冲区
  • CharBuffer —— 存储char类型的缓冲区
  • ShortBuffer —— 存储short类型的缓冲区
  • IntBuffer —— 存储int类型的缓冲区
  • LongBuffer —— 存储long类型的缓冲区
  • FloatBuffer —— 存储float类型的缓冲区
  • DoubleBuffer —— 存储double类型的缓冲区

现在,本人来讲解下 操作Buffer核心API

核心API:

方法名 功能
allocate(int capacity) 申请一块指定capacity大小的 非直接内存缓冲区空间
allocateDirect(int capacity) 申请一块指定capacity大小的 直接内存缓冲区空间
put() 存入数据到缓冲区中
get() 获取缓冲区中的数据
flip() 切换读取数据模式
rewind() 可重复读
clear() 清空缓冲区. 但是缓冲区中的数据依然存在,但是处于“被遗忘”状态
mark() 标记是一个索引,方便后面使用reset()方法跳回这标记处
reset() 使position恢复到之前用mark()方法标记的

除此之外,还有一个方法十分重要:
FileChannel抽象类map()方法
map

它的 作用,是 :

文件区域 直接映射到 内存 中来操作
(操作该对象,也相当于 操作 直接内存空间)


上述 几个Buffer,都采用 相同的实现原理 进行数据管理,只是各自管理的 数据类型不同 而已

那么,本人就以 最常用的 ByteBufferCharBuffer,来大致讲解下 Buffer实现原理

源码剖析:

不同数据的存储 实现:

首先,我们来看看 ByteBufferCharBuffer部分源码
byte

char

我们可以看到:

  • Buffer实现类构造方法,我们API调用者,是无法调用的
    因此,我们若是想 使用Buffer存储数据,就需要调用其 静态方法allocate申请 缓冲区对象存储空间
  • 不同的Buffer实现类,其 数据存储类型 是 其相应类型的数组
    这就是 不同缓冲区,能够实现 不同数据存储原理

在上文中,我们了解了 不同数据的存储原理
那么,缓冲区的基本功能,是如何实现的呢?

“缓冲区”基本功能 实现:

由于本人在上文中,已经明确说明:

所有 NIO 提供的 缓冲区实现,都 继承自 Buffer抽象类

因此,“缓冲区”基本功能 的实现,一定是在 Buffer抽象类
那么,我们来看下 Buffer抽象类部分核心源码
buffer
我们可以看到:

Buffer抽象类 中,有几个 成员属性

那么,这些 Buffer的 基本属性,就是 “缓冲区”基本功能实现原理


现在,本人来讲解下 Buffer基本属性

基本属性:

概念:

  • 容量 (capacity)
    表示 Buffer最大数据容量
    capacity 不能为负,并且 创建后不能更改
  • 限制 (limit)
    第一个不应该 读取或写入的数据的 索引,即 位于 limit 后 的数据 不可读写
    limit 不能为负,并且 不能大于其容量
  • 位置 (position)
    下一个读取或写入数据索引
    position 不能为负,并且 不能大于其限制
  • 标记 (mark)重置 (reset)
    mark是一个 索引
    通过 Buffer 中的 mark()方法 指定 Buffer 中一个 特定的 position ,之后可以通过调用 reset()方法 使得 当前读/写指针 重置 到这个 特定position

属性关系

以上几个基本属性,遵循如下 恒定式

$$
0 <= mark <= position <= limit <= capacity
$$


那么,本人现在通过几张图,来展示下 调用上述API,会发生如何现象:

属性变化 图解:

影响


现在,本人来展示下 有关基本属性API

相关API:

方法名 功能
clear() 清空缓冲区返回对缓冲区的引用
flip() 缓冲区的界限,设置为 当前位置,并将 当前位置 重置为0
capacity() 返回 当前Buffer 的 capacity属性大小
hasRemaining() 判断 缓冲区中 是否还有元素
limit() 返回 当前Buffer 的 limit属性的值
limit(int n) 当前Bufferlimit属性 设置为 n
并 返回 一个 具有 新limit 的 Buffer对象
mark() 将 目标缓冲区 的 mark属性,设置为 当前位置position的值
position() 返回 缓冲区 的 position属性
position(int n) 将 缓冲区 的 position属性 设置为 n,并 返回 修改后的Buffer对象
remaining() 返回 position属性的值 和 limit属性的值,中间的元素个数
reset() position属性 设置为 mark属性 的值
rewind() position属性 设为为 0,将 mark属性 重置为 -1

使用展示:

那么,现在,本人来通过一个例子来展示下这些API 的使用:

package edu.youzg.about_nio.core;

import java.nio.ByteBuffer;

public class Test {

    public static void main(String[] args) {
        String str="youzhuange";
        //申请10字节缓冲区空间
        System.out.println("--------------allocate(10)-------");
        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        int capacity = byteBuffer.capacity();
        int position = byteBuffer.position();
        int limit = byteBuffer.limit();
        System.out.println("capacity:"+capacity);
        System.out.println("position:"+position);
        System.out.println("limit:"+limit);

        //往容器中放数据 put();
        System.out.println("----------put()-----------");
        byteBuffer.put(str.getBytes());
        capacity = byteBuffer.capacity();
        position = byteBuffer.position();
        limit = byteBuffer.limit();
        System.out.println("capacity:" + capacity);
        System.out.println("position:" + position);
        System.out.println("limit:" + limit);

        //读取缓冲区中的数据,切换成读取模式
        System.out.println("---------flip()--------");
        byteBuffer.flip();
        capacity = byteBuffer.capacity();
        position = byteBuffer.position();
        limit = byteBuffer.limit();
        System.out.println("capacity:" + capacity);
        System.out.println("position:" + position);
        System.out.println("limit:" + limit);

        //读取数据 get()
        System.out.println("---------get()---------");
        byte[] bytes = new byte[byteBuffer.limit()];
        byteBuffer.get(bytes);
        System.out.println(new String(bytes, 0, byteBuffer.limit()));
        capacity = byteBuffer.capacity();
        position = byteBuffer.position();
        limit = byteBuffer.limit();
        System.out.println("capacity:" + capacity);
        System.out.println("position:" + position);
        System.out.println("limit:" + limit);

        //可重复读取
        System.out.println("---------rewind()---------");
        byteBuffer.rewind();
        capacity = byteBuffer.capacity();
        position = byteBuffer.position();
        limit = byteBuffer.limit();
        System.out.println("capacity:" + capacity);
        System.out.println("position:" + position);
        System.out.println("limit:" + limit);

        //标记 mark
        System.out.println("---------mark()--------");
        byteBuffer.mark();
        byteBuffer.get(bytes,2,2);
        System.out.println(byteBuffer.position());

        //回到上一次标记position的位置 使用reset();就可以回到上一次标记的位置
        System.out.println("---------reset()--------");
        byteBuffer.reset();
        System.out.println(byteBuffer.position());

        //查询还有没有可读数据
        System.out.println("---------remaining()--------");
        if(byteBuffer.hasRemaining()){
            System.out.println(byteBuffer.remaining()); //remaining()还有多少可读取数据
        }

        //清空缓冲区
        System.out.println("--------clear()----------------");
        //clear()并不是把缓冲区里面的字节数据清掉,而是把这些指针,设置到初始状态
        byteBuffer.clear();
        capacity = byteBuffer.capacity();
        position = byteBuffer.position();
        limit = byteBuffer.limit();
        System.out.println("capacity:" + capacity);
        System.out.println("position:" + position);
        System.out.println("limit:" + limit);

        byte b = byteBuffer.get();
        System.out.println((char)b);

    }

}

本人再来展示下 运行结果

运行结果:

结果1
结果2


NIO 相较于 BIO数据读写效率

但是,在 Buffer 层面,是如何 实现 的呢?
答曰

直接内存 的使用,以及 零拷贝 概念的引入

那么,本人在这里,就来讲解下 直接内存零拷贝 的概念和原理:

直接内存 与 零拷贝:

请观看本人博文 —— 《【NIO】直接内存 与 零拷贝 详解》


那么,至此,NIOBuffer 组件,就讲解完毕了!
完结

posted @ 2020-03-05 10:24  在下右转,有何贵干  阅读(647)  评论(0编辑  收藏  举报