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

你好,我是彤哥,本篇是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()

来个栗子

  1. public class FileChannelTest {
  2. public static void main(String[] args) throws IOException {
  3. // 从文件获取一个FileChannel
  4. FileChannel fileChannel = new RandomAccessFile("D:\\object.txt", "rw").getChannel();
  5. // 分配一个Byte类型的Buffer
  6. ByteBuffer buffer = ByteBuffer.allocate(1024);
  7. // 将FileChannel中的数据读出到buffer中,-1表示读取完毕
  8. // buffer默认为写模式,本文来源工从号彤哥读源码
  9. // read()方法是相对channel而言的,相对buffer就是写
  10. while ((fileChannel.read(buffer)) != -1) {
  11. // buffer切换为读模式
  12. buffer.flip();
  13. // buffer中是否有未读数据
  14. while (buffer.hasRemaining()) {
  15. // 读取数据
  16. System.out.print((char)buffer.get());
  17. }
  18. // 清空buffer,为下一次写入数据做准备
  19. // clear()会将buffer再次切换为写模式
  20. buffer.clear();
  21. }
  22. }
  23. }

allocate()

要获取一个Buffer对象,必须先分配它,每个Buffer类都有一个allocate()方法用于分配Buffer对象。

以下示例分配了一个容量为1024的ByteBuffer对象:

  1. ByteBuffer buffer = ByteBuffer.allocate(1024);

下面是分配了一个容量为48的CharBuffer的对象:

  1. CharBuffer buf = CharBuffer.allocate48);

将数据写入Buffer

将数据写入Buffer有两种形式:

  • 从Channel读出数据并写入Buffer,也叫从Channel读入Buffer
  • 调用Buffer自己的put()方法写入数据

从Channel读入Buffer的示例如下:

  1. int bytesRead = inChannel.readbuf); //读入Buffer

Buffer自己put()写入数据的示例如下:

  1. buf.put127);

当然,put()有很多不同的类型,比如在特定位置写入,写入不同类型的数据等等,可以在IDEA中按F12查看。

flip()

flip()方法用于将Buffer从写模式切换为读模式,position将切换到0位置,且limit将切换到刚才position的位置。

也就是说,position变成了可读数据的首位,limit表示可以读取的最大数据长度。

从Buffer中读取数据

从Buffer中读取数据也有两种形式:

  • 从Buffer读取数据,并写入Channel,也叫作从Buffer写入Channel
  • 调用Buffer自己的get()方法读取数据

从Buffer写入Channel的示例如下:

  1. // 本文来源工从号彤哥读源码
  2. int bytesWritten = inChannel.writebuf);

调用Buffer自己的get()方法读取数据的示例如下:

  1. 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的位置,示例如下:

  1. buffer.mark();
  2. //多次调用buffer.get(),例如在解析过程中。
  3. buffer.reset(); //将位置重新设置为标记。

总结

今天我们学习了Java NIO核心组件Buffer,它经常跟Channel联合起来使用。讲到这里我们一直在使用FileChannel在举例,那么它们到底跟网络编程有什么关系呢?请听下回分解。

参考

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

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

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

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

    你好,我是彤哥,本篇是netty系列的第五篇. 简介 上一章我们一起学习了如何使用Java原生NIO实现群聊系统,这章我们一起来看看Java NIO的核心组件之一--Channel. 思维转变 首先, ...

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

    --日拱一卒,不期而至! 你好,我是彤哥,本篇是netty系列的第七篇. 简介 上一章我们一起学习了Java NIO的核心组件Buffer,它通常跟Channel一起使用,但是它们在网络IO中又该如何 ...

  3. 4. 彤哥说netty系列之Java NIO实现群聊(自己跟自己聊上瘾了)

    你好,我是彤哥,本篇是netty系列的第四篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 上一章我们一起学习了Java中的BIO/NIO/AIO的故事,本章将带着大家一起使 ...

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

    你好,我是彤哥,本篇是netty系列的第三篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 上一章我们介绍了IO的五种模型,实际上Java只支持其中的三种,即BIO/NIO/ ...

  5. 1. 彤哥说netty系列之开篇(有个问卷调查)

    你好,我是彤哥,本篇是netty系列的第一篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 本文主要讲述netty系列的整体规划,并调查一下大家喜欢的学习方式. 知识点 ne ...

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

    你好,我是彤哥,本篇是netty系列的第二篇. 欢迎来我的公从号彤哥读源码系统地学习源码&架构的知识. 简介 本文将介绍linux中的五种IO模型,同时也会介绍阻塞/非阻塞与同步/异步的区别. ...

  7. Netty精粹之JAVA NIO开发需要知道的

    学习Netty框架以及相关源码也有一小段时间了,恰逢今天除夕,写篇文章总结一下.Netty是个高效的JAVA NIO框架,总体框架基于异步非阻塞的设计,基于网络IO事件驱动,主要贡献在于可以让用户基于 ...

  8. Java NIO 核心组件学习笔记

    背景知识 同步.异步.阻塞.非阻塞 首先,这几个概念非常容易搞混淆,但NIO中又有涉及,所以总结一下[1]. 同步:API调用返回时调用者就知道操作的结果如何了(实际读取/写入了多少字节). 异步:相 ...

  9. 一文让你彻底理解 Java NIO 核心组件

    背景知识 同步.异步.阻塞.非阻塞 首先,这几个概念非常容易搞混淆,但NIO中又有涉及,所以总结一下. 同步:API调用返回时调用者就知道操作的结果如何了(实际读取/写入了多少字节). 异步:相对于同 ...

随机推荐

  1. SpringBoot整合RabbitMq(二)

           本文序列化和添加package参考:https://www.jianshu.com/p/13fd9ff0648d RabbitMq安装 [root@topcheer ~]# docker ...

  2. FastJson的使用心得

      本文为早前整理,参考文献已找不到,如有侵权请与我联系,添加参考链接. 一丶基本使用 1.1主要API fastjson入口类是com.alibaba.fastjson.JSON,主要的API是JS ...

  3. MySQL数据库的安装与配置(windows)

    MySQL是目前最为流行的开放源码的数据库,是完全网络化的跨平台的关系型数据库系统,它是由瑞典MySQLAB公司开发,目前属于Oracle公司.任何人都能从Internet下载MySQL软件,而无需支 ...

  4. Scrapy 之如何发送post请求

    import scrapy import json class PostSpider(scrapy.Spider): name = 'post' # allowed_domains = ['www.x ...

  5. 平时服务正常,突然挂了,怎么重启都起不来,查看日志Insufficient space for shared memory file 内存文件空间不足

    Java HotSpot(TM) 64-Bit Server VM warning: Insufficient space for shared memory file:   /tmp/hsperfd ...

  6. Mybaits 源码解析 (七)----- Select 语句的执行过程分析(下篇)(Mapper方法是如何调用到XML中的SQL的?)全网最详细,没有之一

    我们上篇文章讲到了查询方法里面的doQuery方法,这里面就是调用JDBC的API了,其中的逻辑比较复杂,我们这边文章来讲,先看看我们上篇文章分析的地方 SimpleExecutor public & ...

  7. Project Euler 56: Powerful digit sum

    一个古戈尔也就是\(10^{100}\)是一个天文数字,一后面跟着一百个零.\(100^{100}\)更是难以想像的大,一后面跟着两百个零.但是尽管这个数字很大,它们各位数字的和却只等于一.考虑两个自 ...

  8. Kubernetes5-集群上搭建基于redis和docker的留言薄

    一.简介 1.环境依旧是kubernetes之前文章的架构 2.需要docker的镜像 1)php-forntend web 前端镜像 docker.io-kubeguide-guestbook-ph ...

  9. thinkphp5.1长连接-单例模式测试!

    在控制器中  使用以下代码测试 for ($i = 0; $i < 1000; $i++) { $tmp['name'] = 'f_'.$i; $tmp['times'] = date('Y-m ...

  10. m102 SE赛

    这次考试考完试正在刷提交记录的时候,到我这突然oj卡了一下,然后卡了大约10s,再刷出来就发现: