35

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

 4 years ago
source link: https://segmentfault.com/a/1190000021187531
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

<p align="right">——日拱一卒,不期而至!</p>

2aqmeqv.png!web

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

简介

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

概念

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

qEFNzun.png!web

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

属性

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

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

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

meIFv2y.png!web

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

来个栗子

UviuIjJ.jpg!web

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/j...

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

uEv2IvV.png!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK