NIO 缓冲区

2023/6/5 20:21:31

摘要

        Java提供了NIO操作的API,但真正处理NIO流,经常会出现如下代码:

                SocketChannel channel = (SocketChannel) key.channel();
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                while (channel.read(buffer)!=-1){
                    //复位,转化为读模式
                    buffer.flip();
                    while (buffer.hasRemaining()){
                        System.out.println("收到客户端"+channel.socket().getPort()+"的信息:"+ StandardCharsets.UTF_8.decode(buffer).toString());
                    }
                    //清空缓存区,转化为写模式
                    buffer.clear();
                }

可以看出读写经常会出现flip()和clear()等方法,这究极有什么作用?为什么要这么写?

缓冲区的意义

        所有IO都有缓冲区,需要将客户端数据或文件数据读取到内存缓冲区,Java程序才能读取到内存里的客户端或文件内容数据。写入也是如此,Java需要先写入到缓冲区,内核才能把缓冲区数据传输给客户端或文件。

API        

        首先看看Buffer的属性:

容量(Capacity):缓冲区能够容纳的数据元素的最大数量。这一容量在缓冲区创建时被设定,并且永远不能被改变。

上界(Limit):缓冲区的能被写入或读取的最大个数(索引值为该值-1),比如limit为5,capacity为10,则put(5,6)会报错,因为5的代表第6个值,而limit最大只有5,即索引0~4内可写。limit限制了buffer内可写的范围。而capacity限制了buffer的范围。

位置(Position):下一个要被读或写的元素的索引。位置会自动由相应的put( )、get()、flip()、clear()函数更新。

标记(Mark):一个备忘位置。调用 mark( )来设定 mark = postion。调用 reset( )设定 position = mark。mark默认值为-1(JDK8下)。

这些属性在通过实例化或get()和put()等函数操作Buffer时会更新,无论上面怎么变,会向源码里解释的那样:mark <= position <= limit <= capacity。

读取数据

具体测试代码如下:

    public static void main(String[] args) {
        ByteBuffer buffer = ByteBuffer.allocate(100);
        System.out.println(buffer);
        byte[] content = "123".getBytes(Charset.forName("UTF-8"));
        System.out.println("添加的byte数组长度为:"+content.length);
        buffer.put(content);
        System.out.println(buffer);
//        buffer.flip();
//        System.out.println(buffer);
        byte data = buffer.get();
        System.out.println((char)data);
//        System.out.println("buffer的数据为:"+StandardCharsets.UTF_8.decode(buffer).toString());
        System.out.println(buffer);
    }

 打印结果如下:

可以看到:

put():可以将内容添加到buffer中,并将更新position=position+内容长度。

get():是直接读取索引为position的元素,并更新position=position+1。

因此,直接put()添加完内容后用get()读取内容,是读取不到数据的!需要put()完,再调用flip()才能用get()读取到数据,具体如下:

 可以看到:

flip():将limit=position,position=0,mark=-1。这样由于limit=position,buffer是不能添加数据的,因此限制了buffer的写入,而position=0,可以让get()从第一个元素读取。mark=-1,也是给标记重置,虽然mark没啥作用,一般的读写我们也用不上这个mark。

所以,读取buffer数据前一定要调用flip(),否则读取不到数据!

那么读取数据怎么判断position是否超过了limit,可以用remaining()和hasRemaining()判断,具体如下:

 

所以读取数据需要用 flip()限制buffer写入并将position和mark复位,然后调用hasRemaining()对position的位置和limit进行判断,看是否还有数据读取。最后调用get()读取buffer的内容。当然,用其它API读取buffer可以可以的。

写入数据

写入数据上面将了,直接调用put()即可。但问题来了,读取数据需要调用flip(),position会置为0,因此若buffer有历史数据的话,调用flip()再读,会造成历史数据重复读取!所以,Java的API给我们提供了clear()和mark()、reset()方法。

clear()方法会将limit改成capacity,即允许可写,且是整个buffer都可写。将position=0,即后写入的数据将历史数据覆盖。所以调用clear()方法前一定要读完所有数据。

所以一般的buffer读写操作是:

先clear(),再写入()。确保buffer的数据都是新写入的内容,避免使用flip()后读取会读取到历史数据。

当然,有时我们需要保留历史数据,然后再写入,不调用flip()直接读取新写入的数据,这也是可以的,这就需要用到mark了。具体方法如下:

 

 缓冲区内有历史数据,先mark(),让mark=position,然后直接put写入新内容,写完后,调用reset(),使postion回到新内容的起点,再直接调用get()读取数据。这样就只会读新内容,也无需清除历史内容了。

总结

        buffer若不需要追加读,则只需要 get()读取前调用flip()方法从头读取,put()方法前调用clear()重置position从头写入覆盖历史数据。

        buffer需要追加读,则put()前需要调用mark()记录下写入的起始点,写完后直接调用reset()将position调整为新内容的起始点mark。接着再调用get()方法进行读取。


http://www.jnnr.cn/a/369282.html

相关文章

前端-微前端

实现方式&#xff1a; &#xff08;1&#xff09;微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web应用的技术手段及方法策略。 &#xff08;2&#xff09;微前端的核心目标是将巨石应用拆解成若干可以自治的松耦合微应用&#xff0c;这样才能确保微应用真正具…

windows使用YOLOv8训练自己的模型(0基础保姆级教学)

目录 前言 一、使用labelimg制作数据集 1.1、下载labelimg 1.2、安装库并启动labelimg 1.4、制作YOLO数据集 二、使用YOLOv8训练模型 2.1、下载库——ultralytics &#xff08;记得换源&#xff09; 2.2、数据模板下载 2.3、开始训练 1、启动train.py&#xff0c;进行…

重磅音视频开发资料库!!!

为了更好的阅读请前往GitBook 一、前言 这里整理有着丰富的音视频开发的学习资源、开发工具、优秀书籍、教程和开源项目&#xff0c;旨在帮助开发者和爱好者更好地学习、实践和工作。而下图是开发处理的过程&#xff1a; 二、学习技能 语言重要度作用C/C★★★★★作为底层开…

3.30--Redis之常用数据结构--跳表之总结篇(总结篇)------加油呀

跳表 跳表是在链表基础上改进过来的&#xff0c;实现了一种「多层」的有序链表&#xff0c;这样的好处是能快读定位数据 优势是能支持平均 O(logN) 复杂度的节点查找。 只有 Zset 对象的底层实现用到了跳表,zset 结构体里有两个数据结构&#xff1a;一个是跳表&#xff0c;一个…

FasterNet实战:使用FasterNet实现图像分类任务(二)

文章目录训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整算法设置混合精度&#xff0c;DP多卡&#xff0c;EMA定义训练和验证函数训练函数验证函数调用训练和验证方法运行以及结果查看测试热力图可视化展示一…

【数据库管理】⑤归档日志Archive Log

1.日志归档的概述和用途 日志归档是指将数据库的归档日志文件保存到指定的位置&#xff0c;以便在需要时进行恢复和回滚操作。在Oracle数据库中&#xff0c;日志归档是一种重要的备份和恢复策略&#xff0c;可以保证数据库的数据完整性和可靠性。 日志归档的主要用途包括&#…

CVE-2020-1948 Apache dubbo远程命令执行漏洞

预备知识 Dubbo是阿里巴巴公司开源的一个高性能优秀的服务框架&#xff0c;使得应用可通过高性能的RPC实现服务的输出和输入功能&#xff0c;可以和Spring框架无缝集成。 RPC是远程过程调用的简称&#xff0c;广泛应用在大规模分布式应用中&#xff0c;作用是有助于系统的垂直…

Netty组件

Netty组件 EventLoop 事件循环对象 EventLoop本质是一个单线程执行器(同时维护了一个Selector&#xff0c;里面有run方法处理Channel上源源不断的io事件 它的继承关系比较复杂 一条线是继承自 j.u.c.ScheduledExecutorService 因此包含了线程池中所有的方法另一条线是继承…

R 语言基础

R 语言基础 一门新的语言学习一般是从输出 “Hello, World!” 程序开始&#xff0c;R 语言的 “Hello, World!” 程序代码如下&#xff1a; ## 实例&#xff08;helloworld.R&#xff09;myString <- "Hello, World!"print ( myString )以上实例将字符串 “Hell…

SQL注入进阶练习(二)常见绕过手段、防御的解决方案

常见绕过手段、防御的解决方案1.常用SQL注入绕过手段1.1 注释符绕过1.2 大小写绕过1.3 内联注释绕过1.4 双写关键字绕过1.5 特殊编码绕过1.6 空格过滤绕过1.7 过滤 or and xor (异或) not 绕过1.8 过滤等号绕过1.9 过滤大小于号绕过1.10 过滤引号绕过1.11 过滤逗号绕过1.12 过滤…

★LDO相关

1.型号 TPS79501 TPS79301 2.PSRR值&#xff0c;频率 TPS795_50dB&#xff0c;10kHz TPS793_70dB&#xff0c;10kHz 电源抑制比&#xff1a;供电电压纹波对输出电压影响&#xff0c;值越高越好&#xff08;某个频段的AC从输入到输出的衰减程度&#xff0c;衰减越高&#x…

提升集群吞吐量与稳定性的秘诀: Dubbo 自适应负载均衡与限流策略实现解析

作者&#xff1a;刘泉禄 整体介绍 本文所说的“柔性服务”主要是指 consumer 端的负载均衡和 provider 端的限流两个功能。在之前的 Dubbo 版本中&#xff0c;负载均衡部分更多的考虑的是公平性原则&#xff0c;即 consumer 端尽可能平等的从 provider 中作出选择&#xff0c;…

笔记本电脑自带录屏在哪?一步教您找到

案例&#xff1a;怎么找到笔记本电脑上的自带录屏功能&#xff1f; “从网上了解到笔记本电脑有自带的录屏功能&#xff0c;但我不知道笔记本自带的录屏叫什么名字&#xff0c;也不知道笔记本自带录屏在哪。有没有小伙伴知道&#xff1f;” 随着科技的不断进步&#xff0c;越…

【面试题】简单的说说对原型链的了解

大厂面试题分享 面试题库 前后端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;前端面试题库 web前端面试题库 VS java后端面试题库大全 前言 作为Javascript的基础之一&#xff0c;原型一直贯穿我们的JS代码并且成为面试的常考…

基于Python长时间序列遥感数据处理及在全球变化、物候提取、植被变绿与固碳分析、生物量估算与趋势分析等领域中的应用

植被是陆地生态系统中最重要的组分之一&#xff0c;也是对气候变化最敏感的组分&#xff0c;其在全球变化过程中起着重要作用&#xff0c;能够指示自然环境中的大气、水、土壤等成分的变化&#xff0c;其年际和季节性变化可以作为地球气候变化的重要指标。此外&#xff0c;由于…

eclipse上的Java静态分析工具

相比动态测试而言。静态分析效率高&#xff0c;成本较低&#xff0c;对于提高产品质量非常重要。 下面介绍几个elcipse上的静态分析插件 1. findugs a) 安装findbugs插件 1&#xff09;点击菜单 Help ->Eclipse Marketplace 在弹出窗口中的搜索条件中输入 ”findbugs“后…

[素数筛][容斥原理]:埃拉托斯特尼筛法

求解问题&#xff1a;不超过一个给定正整数N的素数的个数 方法介绍&#xff1a; 根据合数的性质&#xff1a;一个合数可以被一个不超过它的平方根的素数整除 这里举例N100&#xff1a; 介绍&#xff1a;为了找出不超过100的素数个数&#xff0c;首先根据合数的性质可以知道…

Docker详解,windows上安装与使用

Hi I’m Shendi Docker详解&#xff0c;windows上安装与使用 Docker详解 Docker 容器是一个开源的应用容器引擎&#xff0c;让开发者可以以统一的方式打包他们的应用以及依赖包到一个可移植的容器中&#xff0c;然后发布到任何安装了docker引擎的服务器上&#xff08;包括流行的…

面试时被问:为什么裁员只裁你,不裁别人,该怎么回答?

面试官总有各种奇奇怪怪的问题&#xff0c;比如这个&#xff1a;为什么裁员裁了你&#xff0c;而不是裁别人&#xff1f;这个充满恶意的问题该怎么回答&#xff1f;网友给出了各种各样的答案&#xff0c;有人说&#xff0c;就说行业动荡&#xff0c;不稳定。有人说&#xff0c;…

Golang每日一练(leetDay0023)

目录 67. 二进制求和 Add Binary &#x1f31f; 68. 文本左右对齐 Text Justification &#x1f31f;&#x1f31f;&#x1f31f; 69. x 的平方根 Sqrt x &#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练 专栏 Python每日一练 专栏 C/C…