专栏首页RabbitMQ实战【NIO】NIO三剑客之一ByteBuffer介绍与使用

【NIO】NIO三剑客之一ByteBuffer介绍与使用

  • 谈及Java NIO,最核心的三个组件就是
    • Channel 通道
    • ByteBuffer 读写缓冲区
    • Selector 多路复用器
  • 核心之一就是在对ByteBuffer的读写操作上。

ByteBuffer为NIO中的字节缓冲区,相对于BIOStream流只支持写入或者读取单向操作,ByteBuffer是双向的,支持读和写。

# 属性与方法简介

  • 类型
    • DirectByteBuffer
      • 使用的是操作系统级别的内存,分配比较慢,但是数据的读写比较快,因为少了一次从系统内存到JVM内存的复制过程
      • 初始化方法:
        • ByteBuffer.allocateDirect(1024 * 4);
    • HeapByteBuffer
      • 使用的是JVM的堆内存,对于JVM来说,分配比较快,但是读写比较慢,因为需要将操作系统内存里的数据复制到JVM内存
      • 初始化方法:
        • ByteBuffer.allocate(1024 * 4);
  • 核心属性
    • capacity
      • ByteBuffer的容量,这个值在ByteBuffer初始化的时候就确定下来了。不论是在读还是在写模式下,这个值都不变。
    • position
      • 写模式下:
        • 该值表示当前写到了ByteBuffer的哪个位置,ByteBuffer初始化时,这个值为0。
        • position的最大值为capacity-1
      • 读模式下:
        • 当从写模式切换到读模式,会将position重置为0,即从ByteBuffer的起始位置开始读取数据。
    • limit
      • 写模式下:
        • limit为最大可写入的数据量,即ByteBuffer的最大容量,值为capacity
      • 读模式下:
        • 当从写模式切换从读模式,limit将会被设置为读模式下的position值,即可读取的最大数据量。
  • 核心方法
    • flip()
      • 将写模式切换为读模式
      • 会触发的对核心属性的操作:
        • position设置为0,即从ByteBuffer起始位置开始读。
        • limit设置为写模式下position的值,即最大可读取的数据量大小。
    • mark()
      • 标记当前position位置
    • reset()
      • position指向上一次mark()所指向的位置,可以从这个位置重复向下读取数据
    • clear()
      • 在逻辑上清空ByteBuffer里的数据,实际上不清空数据
      • 会触发的动作:
        • limit设置为capacity
        • position指向起始位置0
        • 提示:实际上数据并未清理,只是下次是从0的位置开始写入数据,效果上像是数据清空了。
        • 提示:如果ByteBuffer中的数据并未完全读完,调用这个方法将忽略那些未读取的数据。
    • compact()
      • 如果并未读取完ByteBuffer中的数据,调用compact()会将position~limit之间的数据拷贝到ByteBuffer的起始处,并且position为剩余数据量的大小,下次再往ByteBuffer中写入数据时,将在position位置继续往下写,不会覆盖历史数据。
    • hasRemaining()
      • 判断缓冲区中是否还有未读数据
  • 将数据写入ByteBuffer的方式
    • byteBuffer.put(x)
    • channel.read(byteBuffer)
      • 从通道上读取数据,写入缓冲区
  • 从ByteBuffer中读取数据的方式
    • byteBuffer.get()
    • channel.write(bytebuffer)
      • 从缓冲区中读取数据,写入通道

# 代码演示

/**
 * ByteBuffer的代码演示
 *
 * @author futao
 * @date 2020/7/7
 */
public class ByteBufferDemo {
    public static void main(String[] args) {
        // ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024 * 4);
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024 * 4);

        // Q: 初始化之后,这三个值分别是多少呢?
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        //向ByteBuffer写入数据
        byteBuffer.put("hello, 喜欢天文的pony站长~".getBytes(Constants.CHARSET));

        // Q: 向ByteBuffer中写入数据之后,哪些值会发生变化呢?
        System.out.println(StringUtils.repeat("=", 10) + "写入数据之后" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        //将ByteBuffer从写模式调整为读模式
        byteBuffer.flip();

        //将ByteBuffer从写模式调整为读模式之后,ByteBuffer的哪些值会发生变化?
        System.out.println(StringUtils.repeat("=", 10) + "调整ByteBuffer为读模式之后" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        //从ByteBuffer中读取一个字节
        byteBuffer.get();
        // Q: 从ByteBuffer中读取一个字节之后,哪些值会发生变化?
        System.out.println(StringUtils.repeat("=", 10) + "从ByteBuffer中读取一个字节之后" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        // 记录一个标记
        byteBuffer.mark();
        // 继续往下读取
        byteBuffer.get();
        System.out.println(StringUtils.repeat("=", 10) + "记录一个标记之后继续往下读取" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        //重置position到上一次mark()的标记位置
        byteBuffer.reset();
        System.out.println(StringUtils.repeat("=", 10) + "reset之后" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        System.out.println("byteBuffer中是否还有数据:" + byteBuffer.hasRemaining());
        //拷贝未读取的数据到缓冲区最前面
        byteBuffer.compact();
        System.out.println(StringUtils.repeat("=", 10) + "compact之后" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());

        //逻辑上清空数据=>实际上只是指针的变化
        byteBuffer.clear();
        System.out.println(StringUtils.repeat("=", 10) + "clear之后" + StringUtils.repeat("=", 10));
        System.out.println("position: " + byteBuffer.position());
        System.out.println("capacity: " + byteBuffer.capacity());
        System.out.println("limit: " + byteBuffer.limit());
    }
}
  • 先不要看结果,自己思考一下哟,逻辑还是很清晰的~

image.png

是不是与你思考的一致~

# 源代码

https://github.com/FutaoSmile/learn-IO/tree/master/practice/src/main/java/com/futao/practice/chatroom/nio

本文分享自微信公众号 - 喜欢天文(AllUnderControl),作者:UnderControl

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-07-09

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • SpringBoot RabbitMQ实现消息可靠投递

    因为收不到该条消息的ACK。所以一直处于发送中。开启任务调度再次进行投递(投递次数+1,且更新下次投递时间)

    喜欢天文的pony站长
  • SpringBoot RabbitMQ实现消息可靠投递

    因为收不到该条消息的ACK。所以一直处于发送中。开启任务调度再次进行投递(投递次数+1,且更新下次投递时间)

    喜欢天文的pony站长
  • SpringBoot如何做到自动帮我们创建RabbitMQ的Queue和Exchange

    解释:在连接工厂connectionFactory中注册了一个监听器,在监听器中执行了initialize()方法。

    喜欢天文的pony站长
  • NIO学习二-ByteBuffer

    前面我们已经了解到Buffer中,0<=mark<=postion<=limit<=capacity。其中mark是标记,如果为-1时丢弃。postion是当前...

    路行的亚洲
  • 猜生日 Java小游戏

    询问朋友5个问题,找到他出生在一个月的哪一天。每个问题都是询问他的生日是否是5个数字集合中的一个。

    用户7656790
  • 字符串基础类型拓宽的操作,转换成整数值

    Jerry Wang
  • 【程序猿硬核科普】Java获取指定时间年月日 | 时间戳转换bug你的项目中招了吗 | yyyy和YYYY的区别

    最近在知乎看到有篇回答说:跨年导致日期格式YYYY和yyyy导致日期显示Bug的帖子,微信公众号、一些论坛好多中招了,快来看看你的项目里面有没有这个bug吧,哈...

    浩Coding
  • 高并发 threadlocal+countDownLatch+线程池走起来

    gfu
  • 【小家java】Java数值运算 [加减乘除] 精度丢失原因分析,提供保证精度的MathHelper工具类

    有没有一种触目惊心的感觉,感觉回去检查检查自己的代码,有没有一些数值运算吧,哈哈。这个问题相当严重,比如你有9.999999999999元,你的计算机是不会认为...

    YourBatman
  • 面试官拿System.out.println()考了我半个小时?我傻了

    如果你能自己读懂System.out.println(),就真正了解了Java面向对象编程的含义

    Java小咖秀

扫码关注云+社区

领取腾讯云代金券