Java NIO (一)简介

news2024/11/28 23:19:39

1 NIO简介

        在1.4版本之前,Java NIO类库是阻塞IO,从1.4版本开始,引进了新的异步IO库,被称为Java New IO类库,简称为Java NIO。New IO类库的目的 就是要让Java支持非阻塞IO。

        Java NIO类库包含三个核心组件:

        1、Channel(通道)

        2、Buffer(缓冲区)

        3、Selector(选择器)

        理解了上一章高并发IO底层原理,大家会马上识别出来Java NIO属于第三种模型——IO多路复用模型。只不过,Java NIO组件提供了统一的API,为大家屏蔽了底层的操作系统的差异。

        本章,会对上面3个组件展开详细介绍。首先看一下Java NIO和OIO的简单对比。

1.1 NIO和OIO的对比

        在Java中,NIO和OIO的区别主要体现在三个地方:

        1、OIO是面向流的,NIO是面向缓冲区的。在一般的OIO操作中,面向字节流或字符流的IO操作总是以流式的方式顺序地从一个流(Stream)中读取一个或多个字节,因此,我们不能随意改变读取指针的位置。在NIO操作中则不同,NIO中引入了Channel和Buffer的概念。面向缓冲区的读取和写入只需要从通道读取数据到缓冲区中,或将数据从缓冲区写入到通道中。NIO不像OIO那样顺序操作,它可以随意读取Buffer中任意位置的数据。

        2、OIO的操作是阻塞的,而NIO是非阻塞的。OIO操作时,例如调用一个read方法读取一个文件的内容,调用read的线程就会被阻塞,直到read操作完成。在NIO模式中,当调用read方法时,如果此时有数据,则read读取数据并返回,如果此时没有数据,则read也返回直接返回,而不会阻塞当前线程。

        3、OIO没有选择器(Selector)的概念,而NIO有选择器的概念。NIO的实现是基于底层选择器的系统调用的,所以NIO需要底层操作系统提供支持,而OIO不需要选择器。

1.2 通道

        在OIO中,同一个网络连接会关联两个流:一个是输入流(Input Stream),另一个是输出流(Output Stream)。Java应用程序通过这两个流不断地进行输入和输出的操作。

        在NIO中,一个网络连接使用一个通道表示,所有NIO的IO操作都是通过连接通道完成的。一个通道类似于OIO中两个流的结合体,既可以从通道读取数据,也可以向通道写入数据。

1.3 选择器

        首先回顾一下前面介绍的基础知识——IO多路复用(高并发IO底层原理)指的是一个进程/线程可以同时监视多个文件描述符(含socket连接),一旦其中的一个或多个文件描述符可读或者可写,该监听进程/线程就能够进行IO就绪事件的查询。

        在Java应用层面,如何实现对多个文件描述符的监视呢?需要用到一个非常重要的Java NIO组件——选择器。选择器可以理解为一个IO事件的监听与查询器。通过选择器,一个线程可以查询多个通道的IO事件的就绪状态。

        从编程实现维度来说,IO多路复用变成的第一步是把通道注册到选择器中,第二步是通过选择器所提供的事件查询(select)方法来查询这些注册的通道是否有已经就绪的IO事件(例如可读、可写、网络连接完成等)。

        由于一个选择器只需要一个线程进行监控,因此我们可以很简单地使用一个线程,通过选择器去管理多个连接通道。

        与OIO相比,NIO使用选择器的最大优势就是系统开销小。系统不必为每一个网络连接(文件描述符)创建进程/线程,从而大大减少了系统的开销。总之,一个线程负责多个连接通道的IO处理是非常高效的,这种高效来自Java的选择器组件Selector及其底层的操作系统IO多路复用技术的支持。

1.4 缓冲区

        应用程序与通道的交互主要是进行数据的读取和写入。为了完成NIO的非阻塞读写操作,NIO准备了第三个重要的组件——Buffer。所谓通道的读取,就是将数据从通道读取到缓冲区中;通道的写入,就是将数据从缓冲区写入通道中。缓冲区的使用是面向流进行读写操作的OIO所没有的,也就是NIO非阻塞的重要前提和基础之一。

2 Buffer类

        Buffer类是一个抽象类,对应于Java的主要数据类型。在NIO中,有8种缓冲区类,分别是ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer、MappedByteBuffer。前7种Buffer类型覆盖了能在IO中传输的所有Java基本数据类型,第8种类型是一种专门用于内存映射的ByteBuffer类型。不同的Buffer子类可以操作的数据类型能够通过名称进行判断,比如IntBuffer只能操作Integer类型的对象。

        实际上,使用最多的是ByteBuffer(二进制字节缓冲区)类型,后面的章节会看到它的具体使用。

2.1 Buffer类的重要属性

        Buffer的子类会拥有一块内存,作为数据的读写缓冲区,但是读写缓冲区并没有定义在Buffer基类中,而是定义在具体的子类中。例如,ByteBuffer子类就拥有一个byte[]类型的数组成员

final byte[] hb; 

可以作为自己的读写缓冲区,数据的元素类型与Buffer子类的操作类型相对应。为了记录读写的状态和位置,Buffer类额外提供了一些重要的属性,其中有三个重要的成员属性:capacity(容量)、position(读写位置)和limit(读写的限制)。

        1、capacity属性

        Buffer类的capacity属性表示内存容量的大小。一旦写入的对象数量超过了capacity,缓冲区就满了,不能在写入。capacity属性一旦初始化,就不能再改变。原因是什么呢?Buffer类的对象在初始化时会按照capacity分配内部数组的内存,在数据内存分配好之后,它的大小就不能改变了。前面讲到,Buffer类是一个抽象类,Java不能直接用来创建对象。在具体使用的时候,必须使用Buffer的某个子类,例如DoubleBuffer子类,该子类能写入的数据类型是double,如果在创建实例时,其capacity是100,那么最多可以写入100个double类型的数据。

        【注意】capacity不是指内部的内存块byte[]数组的字节数量,而是指能写入的数据对象的最大限制数量。

        2、position属性

        position表示当前的位置。position属性的值与缓冲区的读写模式有关,在不同的模式下,position属性值的含义是不同的,在缓冲区进行读写的模式改变时,position值会进行相应的调整。

        在写模式下,position值的变化规则如下:

        (1)在刚进入写模式时,position值为0,表示当前的写入位置从头开始。

        (2)每当一个数据写到缓冲区后,position就会向后移动到下一个可写的位置。

        (3)初始的position值为0,最大可写值为limit-1。当position值达到limit时,缓冲区就已经无可用空间写入了。

        在读模式下,position值的变化规则如下:

        (1)当缓冲区刚开始进入读模式时,position会被重置为0.

        (2)当从缓冲区读取时,也是从position位置开始读。读取数据后,position向前移动到下一个可读的位置。

        (3)在读模式下,limit表示可读数据的上线。position的最大值为最大可读上线limit,当position达到limit时表示缓冲区已经无数据可读。

        Buffer的读写模式具体如何切换呢?当新建了一个缓冲区实例时,缓冲区处于写模式,这时可以写入数据。在数据写入完成后,如果要从缓冲区读取数据,就要进行模式的切换,可以调用flip()方法将缓冲区变成读模式,flip为翻转的意思。再从写模式到读模式的翻转过程中,position和limit属性值会进行调整,具体的规则是:

        (1)limit属性被设置成写模式时的position值,表示可以读取的最大数据位置。

        (2)position由原来的写入位置变成新的可读位置,也就是0,表示可以从头开始读。

        3、limit属性

        limit属性表示可以写入或读取的数据最大上限,其属性值的具体含义也与缓冲区的读写模式有关。在不同的模式下,limit值的含义是不同的,具体分为以下两种情况。

        (1)在写模式下,limit属性值的含义为可以写入的数据最大上限。在刚进入写模式时,limit的值会被设置成缓冲区的capacity值,表示可以一直将缓冲区的容量写满。

        (2)在读模式下,limit值的含义为最大能从缓冲区读取多少数据。

        一般来说,在进行缓冲区操作时是先写入再读取。当缓冲区写入完成后,就可以开始从Buffer读取数据,调用flip()方法,这时limit的值也会进行调整。具体如何调整呢?将写模式下的position值设置成读模式下的limit值,也就是说,将之前写入的最大数量作为可以读取数据的上限值。

        Buffer在翻转时的属性值调整主要涉及position,limit两个属性,但这种调整比较微妙,不太好理解,下面举例说明:

        首先,创建缓冲区。新创建的缓冲区处于写模式,其position值为0,limit值为最大容量capacity。

        然后,向缓冲区写数据。每写入一个数据,position向后面移动一个位置,也就是position的值加1。这里假定写入了5个数,当写入完成后,position的值为5.

        最后,使用flip()方法将缓冲区切换到读模式。limit的值会先被设置成写模式的position值,所以新的limit的值是5,表示可以读取数据的最大上限是5.之后调整position值,新的position会被重置为0,表示可以从0开始读。

        缓冲区切换到读模式后就可以从缓冲区读取数据了,一直到缓冲区的数据读取完毕。

        除了以上capacity、limit、position三个重要的成员属性外,Buffer还有一个比较重要的标记属性:mark(标记)属性。该属性的大致作用是:在缓冲区操作过程中,可以将当前的position值临时存入mark属性中,需要的时候,再从mark中取出暂存的标记值,恢复到position属性中,重新从position位置开始处理。

        下面用表总结一下Buffer类的四个重要属性。

508f47c9644b4ddcbb2f80038a3a60a5.jpeg

 

       

   

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1392504.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Vue3中provide,inject使用

一,provide,inject使用: 应用场景:向孙组件传数据 应用Vue3碎片: ref,reactive,isRef,provide, inject 1.provide,inject使用 a.爷组件引入 import {ref,provide} from vue const drinkListre…

LeetCode、2542. 最大子序列的分数【中等,排序+小顶堆】

文章目录 前言LeetCode、2542. 最大子序列的分数【中等,排序小顶堆】题目及类型思路及代码实现 资料获取 前言 博主介绍:✌目前全网粉丝2W,csdn博客专家、Java领域优质创作者,博客之星、阿里云平台优质作者、专注于Java后端技术领…

安泰射频功率放大器的主要的类型及主要参数

射频功率放大器是一种将射频信号放大到更高功率级别的电子设备。根据其工作原理和应用要求,射频功率放大器可以分为不同的类型。下面西安安泰将介绍一些常见的射频功率放大器类型和相关的主要参数。 A类功率放大器:A类功率放大器是一种广泛使用的线性放大…

Navicat教程

下载连接(无限使用版) 链接:https://pan.baidu.com/s/1IprYLRv0bSnW-XKn0trRtw 提取码:j6qx 连接使用 1.1 连接数据库 打开navicat,点击连接,选择数据库 1.2 操作数据库 右键连接,点击新建数…

Excel 动态可视化图表分享

AIGC ChatGPT 职场案例 AI 绘画 与 短视频制作 PowerBI 商业智能 68集 数据库Mysql 8.0 54集 数据库Oracle 21C 142集 Office 2021实战应用 Python 数据分析实战, ETL Informatica 数据仓库案例实战 Excel 2021实操 100集, Excel 2021函数大全 80集 Exc…

【C++】入门C++前想要了解的小知识

个人主页 : zxctsclrjjjcph 文章封面来自:艺术家–贤海林 如有转载请先通知 目录 1. 前言2. 什么是C3. C的发展史4. C的重要性4.1 语言的使用广泛度4.2 在工作领域中4.3 在校招领域中 5. 如何学习C5.1 看看别人怎么学习的5.2 自己怎么学 1. 前言 今天开…

【FastAPI】请求体

在 FastAPI 中,请求体(Request Body)是通过请求发送的数据,通常用于传递客户端提交的信息。FastAPI 使得处理请求体变得非常容易。 请求体是客户端发送给 API 的数据。响应体是 API 发送给客户端的数据 注:不能使用 …

docker容器和常用命令

1.什么是容器 容器是隔离的环境中运行的一个 进程 , 如果进程结束 , 容器就会停止. 细致: 容器的隔离环境 , 拥有自己的 ip 地址 , 系统文件 , 主机名 , 进程管理 , 相当于一个 mini的系统 2.容器 vs 虚拟机 3.Docker极速上手指南 #1.安装相关依赖. sudo yum install -y …

链表的中间节点

链表的中间节点 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/middle-of-the-…

python之AI视频去码图片修复CodeFormer,把你的老照片变漂亮

CodeFormer介绍 CodeFormer是由南洋理工大学-商汤科技联合研究中心S-Lab在NeurIPS 2022上提出的一种基于VQGANTransformer的人脸复原模型。该方法基于预训练VQGAN离散码本空间,改变复原任务的固有范式,将人脸复原任务转成Code序列的预测任务&#xff0c…

DOM 的 diff 算法

经典面试题: 1)react/vue中的 key 有什么作用?(key的内部原理是什么?) 2)为什么遍历列表时,key 最好不用 index? 1、虚拟dom中key的作用: 1) 简…

SpringBoot新手入门完整教程和项目示例

文章目录 SpringBoot新手入门完整教程和项目示例1、SpringBoot简介2、Spring Boot的核心功能?(优点)3、SpringBoot与SpringMVC 的区别?4、构建SpringBoot项目4.1、在官网自动生成下载spring boot项目4.2、手动使用maven创建Spring…

vue中使用高德地图,根据类型显示不同点,点击出现弹框居中显示,并在可视化区域显示所有点

效果图 一、安装 vue-amap 插件 npm install vue-amap --save二、vue页面 <template> <div><ul styledisplay:flex;><liv-for"(item, index) in checkList":key"index"click"onClick(item)":class"item.checked ?…

直观全面的数据库管理 RazorSQL

RazorSQL是一款功能强大的数据库管理工具&#xff0c;适用于各种数据库系统&#xff0c;如MySQL、Oracle、SQL Server、SQLite等。它提供了一个直观的界面&#xff0c;让用户能够轻松地管理数据库&#xff0c;包括查询、编辑、创建和管理数据库对象等操作。RazorSQL还支持多种S…

使用composer生成的DMG和PKG格式软件包有何区别

在使用Composer从包源构建软件包时候&#xff0c;有两种不同类型的包&#xff1a;PKG和DMG。你知道两者之间的区别吗? 以及如何选取吗&#xff1f; 每种格式都有各自的优势具体取决于软件包的预期用途以及用于部署软件包的工具。下面我们来了解一下PKG和DMG格式的区别和用途。…

论文笔记:信息融合的门控多模态单元(GMU)

整理了GMU&#xff08;ICLR2017 GATED MULTIMODAL UNITS FOR INFORMATION FUSION&#xff09;论文的阅读笔记 背景模型实验 论文地址&#xff1a; GMU 背景 多模态指的是同一个现实世界的概念可以用不同的视图或数据类型来描述。比如维基百科有时会用音频的混合来描述一个名人…

详解Vite创建Vue3项目+vue-router+ts+vite+element-plus

前言 在之前的文章中写过“Vue3TSElementPlus的安装和使用教程【详细讲解】”&#xff0c;但那篇文章写的是创建vue3的项目没有使用到Vite构建工具进行创建还是使用的常规webpacket构建工具进行创建的。提到Vite和webpacket的时候我们可以简单说一下。Vite 和 Webpack 都是现代…

SpringBoot框架自定义解析配置项占位符(${})获取配置

一、前言 在最近项目开发中&#xff0c;有一个需求&#xff0c;针对自定义注解中字符串属性值的设置需要支持使用”${xx}“占位符获取在SpringBoot框架配置文件中配置项对应的属性值&#xff0c;而且支持多个”${xx}“标识的配置任意拼接。 二、功能实现 从实现思路上说还是…

nas-群晖docker查询注册表失败解决办法(平替:使用SSH命令拉取ddns-go)

一、遇到问题 群晖里面的docker图形化界面现在不能直接查询需要下载的东西&#xff0c;原因可能就是被墙了&#xff0c;那么换一种方式使用SSH命令下载也是可以的&#xff0c;文章这里以在docker里面下载ddns-go为例子。 二、操作步骤 &#xff08;一&#xff09;打开群晖系统…

STM32 TIM输出比较、PWM波形

单片机学习&#xff01; 目录 一、输出比较简介 二、PWM简介 三、输出比较通道 3.1通用定时器的输出比较部分电路 3.2高级定时器的输出比较部分电路 四、输出模式控制器 五、PWM基本结构 六、PWM参数计算 总结 前言 文章讲述STM32定时器的输出比较功能&#xff0c;它主…