6. 彤哥说netty系列之Java NIO核心组件之Buffer

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

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

简介

上一章我们一起学习了Java NIO的核心组件Channel,它可以看作是实体与实体之间的连接,而且需要与Buffer交互,这一章我们就来学习一下Buffer的特性。

概念

Buffer用于与Channel交互时使用,通过上一章的学习我们知道,数据从Channel读取到Buffer,或者从Buffer写入Channel。

Buffer本质上是一个内存块,可以向里面写入数据,或者从里面读取数据,在Java中它被包装成了Buffer对象,并提供了一系列的方法用于操作这个内存块。

属性

为了更好地理解Buffer的数据结构,我们必须熟悉它的三个常用属性:

  • capacity:容量
  • position:当前位置
  • limit:限制长度

在读模式和写模式下,position和limit的位置有所不同,见下图:

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()

来个栗子

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

最后,也欢迎来我的工从号彤哥读源码系统地学习源码&架构的知识。

原文地址:https://www.cnblogs.com/tong-yuan/p/11980067.html

时间: 2024-08-11 20:00:41

6. 彤哥说netty系列之Java NIO核心组件之Buffer的相关文章

5. 彤哥说netty系列之Java NIO核心组件之Channel

你好,我是彤哥,本篇是netty系列的第五篇. 简介 上一章我们一起学习了如何使用Java原生NIO实现群聊系统,这章我们一起来看看Java NIO的核心组件之一--Channel. 思维转变 首先,我想说的最重要的一个点是,学习NIO思维一定要从BIO那种一个连接一个线程的模式转变成多个连接(Channel)共用一个线程来处理的这种思维. 1个Connection = 1个Socket = 1个Channel,这几个概念可以看作是等价的,都表示一个连接,只不过是用在不同的场景中. 如果单从阻塞

7. 彤哥说netty系列之Java NIO核心组件之Selector

<p align="right">--日拱一卒,不期而至!</p> 你好,我是彤哥,本篇是netty系列的第七篇. 简介 上一章我们一起学习了Java NIO的核心组件Buffer,它通常跟Channel一起使用,但是它们在网络IO中又该如何使用呢,今天我们将一起学习另一个NIO核心组件--Selector,没有它可以说就干不起来网络IO. 概念 我们先来看两段Selector的注释,见类java.nio.channels.Selector. 注释I A mul

3. 彤哥说netty系列之Java BIO NIO AIO进化史.md

你好,我是彤哥,本篇是netty系列的第三篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 先说两个事 (1)上周五的那篇文章发重复了,是定时任务设置错误导致,给大家带来干扰,这里说声抱歉. (2)之前的问卷调查结果出来了,认为先讲案例的票数较多,所以后面的文章都是先讲案例,再以案例展开讲解组件. 简介 上一章我们介绍了IO的五种模型,实际上Java只支持其中的三种,即BIO/NIO/AIO. 本文将介绍Java中这三种IO的进化史,并从使用的角度剖析它们背后的故事. Java BI

2. 彤哥说netty系列之IO的五种模型

你好,我是彤哥,本篇是netty系列的第二篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 本文将介绍linux中的五种IO模型,同时也会介绍阻塞/非阻塞与同步/异步的区别. 何为IO模型 对于一次IO操作,数据会先拷贝到内核空间中,然后再从内核空间拷贝到用户空间中,所以一次read操作,会经历两个阶段: (1)等待数据准备 (2)数据从内核空间拷贝到用户空间 基于以上两个阶段就产生了五种不同的IO模式. 阻塞IO 从进程发起IO操作,一直等待上述两个阶段完成. 两阶段一起阻塞

Java NIO中的Buffer 详解

Java NIO中的Buffer用于和NIO通道进行交互.如你所知,数据是从通道读入缓冲区,从缓冲区写入到通道中的.缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存.这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存. Buffer的基本用法使用Buffer读写数据一般遵循以下四个步骤: 写入数据到Buffer    调用flip()方法    从Buffer中读取数据    调用clear()方法或者compact()方法 当向buffer写入数据时,

(四:NIO系列) Java NIO Selector

出处:Java NIO Selector 1.1. Selector入门 1.1.1. Selector的和Channel的关系 Java NIO的核心组件包括: (1)Channel(通道) (2)Buffer(缓冲区) (3)Selector(选择器) 其中Channel和Buffer比较好理解 ,联系也比较密切,他们的关系简单来说就是:数据总是从通道中读到buffer缓冲区内,或者从buffer写入到通道中. 选择器和他们的关系又是什么? 选择器(Selector) 是 Channel(通

java NIO中的buffer和channel

缓冲区(Buffer):一,在 Java NIO 中负责数据的存取.缓冲区就是数组.用于存储不同数据类型的数据 根据数据类型不同(boolean 除外),提供了相应类型的缓冲区:ByteBufferCharBufferShortBufferIntBufferLongBufferFloatBufferDoubleBuffer 上述缓冲区的管理方式几乎一致,通过 allocate() 获取缓冲区 二.缓冲区存取数据的两个核心方法:put() : 存入数据到缓冲区中get() : 获取缓冲区中的数据

Java NIO学习之Buffer

Java NIO的核心部件: Buffer Channel Selector Buffer 是一个数组,但具有内部状态.如下4个索引: capacity:总容量 position:下一个要读取/写入的元素索引 limit:限制,第一个不能读取/写入的元素索引 mark:位置标记,重置position //通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position.之后可以通过调用Buffer.reset()方法恢复到这个position// 0 <= mark <

Java NIO 系列教程(转)

原文中说了最重要的3个概念,Channel 通道Buffer 缓冲区Selector 选择器其中Channel对应以前的流,Buffer不是什么新东西,Selector是因为nio可以使用异步的非堵塞模式才加入的东西.以前的流总是堵塞的,一个线程只要对它进行操作,其它操作就会被堵塞,也就相当于水管没有阀门,你伸手接水的时候,不管水到了没有,你就都只能耗在接水(流)上.nio的Channel的加入,相当于增加了水龙头(有阀门),虽然一个时刻也只能接一个水管的水,但依赖轮换策略,在水量不大的时候,各