初识Java中的NIO

1.概述

  Java NIO 全称java non-blocking IO ,是指 JDK 提供的新 API。从 JDK1.4 开始,Java 提供了一系列改进的输入/输出新特性,被统称为 NIO(即 New IO),是同步非阻塞的。NIO采用内存映射文件的方式来处理输入输出,NIO将文件或文件的一段区域映射到内存中,这样就可以像访问内存一样访问文件。NIO与原来的IO有同样的作用,但是使用的方式完全不同, NIO支持面向缓冲区的、基于通道的IO操作。 NIO将以更加高效的方式进行文件的读写操作。

2.NIO 三大核心原理示意图

  NIO 有三大核心部分:Channel(通道)Buffer(缓冲区)Selector(选择器)。NIO是面向缓冲区编程。数据读取到一个buffer中(缓冲区),需要时可在缓冲区内前后移动,增加了处理过程中的灵活性,使用它可提供非阻塞式的伸缩性网络。对于非阻塞式的理解:通俗来说就是一个线程可以处理多个操作
在这里插入图片描述

  • 每个 channel 都会对应一个 Buffer;
  • Selector 对应一个线程, 一个线程对应多个 channel(连接);
  • 每个 channel 都注册到 Selector选择器上;
  • Selector不断轮询查看Channel上的事件, 根据不同的事件完成不同的操作;
  • Buffer 就是一个内存块 , 底层是一个数组,NIO的Buffer是可以读也是可以写的,channel是双向的。

2.1 缓冲区Buffer

  缓冲区实际上是一个容器对象,底层是一个数组,在NIO库中,所有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的; 在写入数据时,它也是写入到缓冲区中的;任何时候访问 NIO 中的数据,都是将它放到缓冲区中。而在面向流I/O系统中,所有数据都是直接写入或者直接将数据读取到Stream对象中。具体看下面这张图就理解了:

在这里插入图片描述
上图描述了从一个客户端向服务端发送数据,然后服务端接收数据的过程。客户端发送数据时,必须先将数据存入Buffer中,然后将Buffer中的数据写入通道。服务端这边接收数据必须通过Channel将数据读入到Buffer中,然后再从Buffer中取出数据来处理。

  在NIO中,所有的缓冲区类型都继承于抽象类Buffer,最常用的就是ByteBuffer,对于Java中的基本类型,基本都有一个具体Buffer类型与之相对应,它们之间的继承关系如下图所示:
在这里插入图片描述

注:可以看到除了Boolean 类型外,其它都有对应的Buffer。

Buffer四个成员变量的说明

private int mark = -1;
private int position = 0;
private int limit;
private int capacity;

一般来说,四个属性的关系应该属于:

​ 0 <= mark <= position <= limit <= capacity

属性说明
capacity容量,即可以容纳的最大数据量;在缓冲区创建时设置并且不能改变
limit上限,缓冲区中当前的数据量
position位置,缓冲区中下一个要被读或写的元素的索引
mark调用mark()方法来设置mark=position,再调用reset()可以让position恢复到mark标记的位置,即position=mark

在这里插入图片描述

由于缓存区是读写共存,所以不同的模式下,这两个变量的值也具有不同的意义。

写模式下,所谓写模式就是将缓存区中的内容写入通道(buffer–>channel)。position 代表下一个字节应该被写出去的字节在缓存区中的位置,limit 表示最后一个待写字节在缓存区的位置。

读模式下,所谓读模式就是从通道读取数据到缓存区(channel–>buffers)。position 代表下一个读出来的字节应当存储在缓存区的位置,limit 等于 capacity。

2.2 通道channel

  Channel和传统IO中的Stream很相似。虽然相似,但是有很大的区别,主要区别为:通道是双向的,通过一个Channel既可以读,也可以写;而Stream只能进行单向操作,通过一个Stream只能进行读或者写,比如:InputStream只能进行读取操作,OutputStream只能进行写操作。但是通道和流一样都是需要基于物理文件的,而每个流或者通道都通过文件指针操作文件,这里说的「通道是双向的」也是有前提的,那就是通道基于随机访问文件『RandomAccessFile』的可读可写文件指针。

  通道是一个对象,通过它可以读取和写入数据,当然了所有数据都通过Buffer对象来处理。我们永远不会将字节直接写入通道中,相反是将数据写入包含一个或者多个字节的缓冲区。同样不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。

比喻:通常把IO比喻成为水流,管道就是水流的通道;把NIO比喻为火车的轨道,然后缓冲区就是上面的火车。
在NIO中,提供了多种通道对象,而所有的通道对象都实现了Channel接口。它们之间的继承关系如下图所示:
在这里插入图片描述

  Channel(通道)表示到实体如硬件设备、文件、网络套接字或可以执行一个或多个不同I/O操作的程序组件的开放的连接。所有的Channel都不是通过构造器创建的,而是通过传统的节点InputStream、OutputStream的getChannel方法来返回响应的Channel。

  Channel中最常用的三个类方法就是map、read和write,其中map方法用于将Channel对应的部分或全部数据映射成ByteBuffer,而read或write方法有一系列的重载形式,这些方法用于从Buffer中读取数据或向Buffer中写入数据。

2.3 选择器Selector

  Selector类是NIO的核心类,**Selector能够检测多个注册的通道上是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理。**这样一来,只是用一个单线程就可以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间的上下文切换导致的开销。

  与Selector有关的一个关键类是SelectionKey,一个SelectionKey表示一个到达的事件,这2个类构成了服务端处理业务的关键逻辑。

  Selector选择器可以理解为一个IO事件的监听与查询器,通过选择器,一个线程可以查询多个通道的IO事件的就绪状态。

什么是IO事件?

表示通道某种IO操作已经就绪或者说已经做好了准备。

例如:如果一个新Channel连接建立成功,就会在Server Socket Channel上发生一个IO事件,代表一个新连接一个准备好,这个IO事件叫做“接收就绪”事件。

NIO定义了四个事件:SelectionKey.OP_ACCEPT、SelectionKey.OP_CONNECT、SelectionKey.OP_READ、SelectionKey.OP_WRITE

3.使用案例

3.1服务端

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Set;public class Server {public static void main(String[] args) throws Exception {//创建Selector对象,管理多个channelSelector selector = Selector.open();//创建ServerSocketChannel对象(绑定主机名和端口号)ServerSocketChannel ssc = ServerSocketChannel.open().bind(new InetSocketAddress("localhost", 8080));//设置服务为非阻塞(必须配置,否则报错)ssc.configureBlocking(false);// 通道可支持的操作:支持新的连接 监听ACCEPT事件// ServerSocketChannel仅支持接受新连接,因此此方法返回SelectionKey.OP_ACCEPT 。int ops = ssc.validOps();//将通道注册到selector 等待连接SelectionKey selectKy = ssc.register(selector, ops, null);for (; ; ) {// 无条件的循环// 检测当前选择器注册通道是否有就绪事件,如果没有就阻塞,有事件,线程才会恢复运行int noOfKeys = selector.select();if (noOfKeys <= 0) {continue;}// 获取就绪的事件,即为选择器键集合Set selectedKeys = selector.selectedKeys();Iterator itr = selectedKeys.iterator();while (itr.hasNext()) {SelectionKey ky = (SelectionKey) itr.next();//选择键事件为接收就绪事件if (ky.isAcceptable()) {//获取客户端连接通道SocketChannel client = ssc.accept();//配置客户端为非阻塞client.configureBlocking(false);//重点关注:READ事件client.register(selector, SelectionKey.OP_READ);// 选择键为READ事件} else if (ky.isReadable()) {//获取客户端SocketChannel通道SocketChannel client = (SocketChannel) ky.channel();//设置缓冲区大小ByteBuffer buffer = ByteBuffer.allocate(256);//客户端读取缓冲区数据client.read(buffer);String output = new String(buffer.array()).trim();System.out.println("接收客户端信息: " + output);ByteBuffer buffer1 = ByteBuffer.wrap(("服务端时间戳:"+System.currentTimeMillis()).getBytes(StandardCharsets.UTF_8));//将数据写回缓冲区client.write(buffer1);}itr.remove();// 将选择键清空,防止下次循环时被重复处理}}}
}

3.2 客户端

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;public class Client {public static void main(String[] args) throws Exception {// 创建InetSocketAddress对象,绑定主机名和端口号InetSocketAddress hA = new InetSocketAddress("localhost", 8080);//获取客户端SocketChannel通道SocketChannel client = SocketChannel.open(hA);System.out.println("The Client is sending messages to server...");for (; ; ) {//实例化缓冲区对象ByteBuffer buffer = ByteBuffer.wrap(("客户端时间戳:" + System.currentTimeMillis()).getBytes(StandardCharsets.UTF_8));//数据写入缓冲区client.write(buffer);//清空缓冲区buffer.clear();//设置新缓冲区的大小ByteBuffer buffer1 = ByteBuffer.allocate(256);//读取新缓冲区数据client.read(buffer1);//打印结果到控制台System.out.println("接收服务器消息:" + new String(buffer1.array(), StandardCharsets.UTF_8).trim());TimeUnit.SECONDS.sleep(3);}}
}

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

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

相关文章

Linux Shell:`awk` 命令

Linux Shell&#xff1a;awk 命令 awk 是一种强大的文本分析工具&#xff0c;广泛用于文本处理、数据提取和报告生成。它使用自己的编程语言来处理文件中的数据。在 Linux Shell 中&#xff0c;awk 命令能够执行复杂的模式匹配、编辑和分析任务。本文将介绍 awk 的基础用法、高…

激光雷达和相机的联合标定工具箱[cam_lidar_calibration]介绍

激光雷达和相机的联合标定工具箱[cam_lidar_calibration]介绍 写在前面安装过程调试过程标定成功可视化展示 写在前面 激光雷达和相机联合标定工具 论文地址&#xff1a;https://ieeexplore.ieee.org/stamp/stamp.jsp?tp&arnumber9564700 github地址: https://github.com…

系统架构评估_2.SAAM方法

SAAM&#xff08;Scenarios-based Architecture Analysis Method&#xff09;是卡耐基梅隆大学软件工程研究所&#xff08;SEI at CMU&#xff09;的Kazman等人于1983年提出的一种非功能质量属性的架构分析方法&#xff0c;是最早形成文档并得到广泛使用的软件架构分析方法。最…

RabbitMQ Docker 安装与应用

1.官方镜像 该镜像包含用户操作界面 2.Docker运行&#xff0c;并设置开机自启动 docker run -d --restartalways --name rabbitmq -p 5672:5672 -p 15672:15672 rabbitmq:3.10-management 默认登录账户和密码 guest 3、使用 队列和交换机绑定

【CicadaPlayer】视频切换/音视频同时切换

G:\CDN\all_players\CicadaPlayer-github-0.44\mediaPlayer\SuperMediaPlayer.hCicadaPlayer https://github.com/alibaba/CicadaPlayer可以clone 整个仓库的历史 git clone --bare https://github.com/username/project.git整体架构 :根据这个更容易理解:切换就是judgeFunc…

zookeeper中的znode节点的一些功能和应用

zookeeper是一个挺好玩的东西 有着独特的选举机制&#xff0c;一般在中小型集群中&#xff0c;zookeeper一般装在三个节点 其中只有一个节点对外提供服务&#xff0c;处于leader状态&#xff0c;另外两台未follower状态 这得益于zookeeper独特的选举机制&#xff0c;可以保证le…

Ubuntu22.04平台编译完美解决问题“error: GLSL 4.5 is not supported.”【GLSL(OpenGL着色器语言)】

GLSL介绍 GLSL&#xff08;OpenGL着色器语言&#xff09;是用于编写OpenGL着色器程序的语言。GLSL 4.5 是 GLSL 的一个版本&#xff0c;引入了许多新的特性和改进&#xff0c;旨在提高着色器编程的灵活性和性能。GLSL 4.5 工具通常是用于编写、调试和优化 GLSL 4.5 着色器代码…

【TI毫米波雷达】官方工业雷达包的生命体征检测环境配置及避坑(Vital_Signs、IWR6843AOPEVM)

【TI毫米波雷达】官方工业雷达包的生命体征检测环境配置及避坑&#xff08;Vital_Signs、IWR6843AOPEVM&#xff09; 文章目录 生命体征基本介绍IWR6843AOPEVM的配置上位机配置文件避坑上位机start测试距离检测心跳检测呼吸频率检测空环境测试 附录&#xff1a;结构框架雷达基…

如何在 Node.js 中使用 bcrypt 对密码进行哈希处理

在网页开发领域中&#xff0c;安全性至关重要&#xff0c;特别是涉及到用户凭据如密码时。在网页开发中至关重要的一个安全程序是密码哈希处理。 密码哈希处理确保明文密码在数据库受到攻击时也难以被攻击者找到。但并非所有的哈希方法都是一样的&#xff0c;这就是 bcrypt 突…

AcWing刷题-公约数

公约数 代码 from math import gcd a, b map(int, input().split()) p int(input()) max_gcd gcd(a, b) res []for i in range(1, int(max_gcd**0.5)1):if max_gcd % i 0:res.append(i) res.append(max_gcd//i) res sorted(set(res))for _ in range(p):l, r map(int,…

绘图工具 draw.io / diagrams.net 免费在线图表编辑器

拓展阅读 常见免费开源绘图工具 OmniGraffle 创建精确、美观图形的工具 UML-架构图入门介绍 starUML UML 绘制工具 starUML 入门介绍 PlantUML 是绘制 uml 的一个开源项目 UML 等常见图绘制工具 绘图工具 draw.io / diagrams.net 免费在线图表编辑器 绘图工具 excalidr…

mac老版本如何升级到最新版本

mac老版本如何升级到最新版本 老macbook升级新版本&#xff08;Big sur、Monterey&#xff09; 首先介绍我的电脑的机型及情况&#xff1a; 2015年初的MacBook Air 处理器是1.6Hz 双核Interl Core i5 内存4G 老版本只能升到10.13 想要升到最高版本的原因&#xff1a;想要注册…

JJVM类的加载过程

类的加载过程 一个java文件从被加载到被卸载这个生命过程&#xff0c;总共要经理五个阶段&#xff0c;JVM将类加载过程分为&#xff1a;&#xff08;加链初使卸&#xff09; 1. 加载 首先通过一个类的全限定名来获取此类的二进制字节流&#xff1b;其次将这个字节流所代表的静…

Centos7下docker删除容器与镜像

个人记录 查看容器 docker ps -a停止容器运行 docker stop jenkins卸载容器 docker rm jenkins查看镜像 docker images卸载镜像 docker rmi IMAGE ID查看容器与镜像是否卸载完毕 docker images docker ps -a

avro c++编译与使用

一、arvo介绍 Avro 是 Hadoop 中的一个子项目&#xff0c;也是一个数据序列化系统&#xff0c;其数据最终以二进制格式&#xff0c;采用行式存储的方式进行存储。 Avro提供了&#xff1a; 1)、丰富的数据结构。 2)、可压缩、快速的二进制数据格式。 3)、一个用来存储持久化数据…

海外媒体宣发,穿透与世界的交流 - “保姆级”教程 - 大舍传媒

1. 引言 在当今高度信息化的世界&#xff0c;境外媒体宣发已经成为企业、品牌和政府机构推广自身形象、扩大影响力的重要手段。如何在国际舞台上有效传播信息&#xff0c;提高国际知名度&#xff0c;成为了许多组织面临的重要课题。大舍传媒凭借多年的境外媒体宣发经验&#x…

TYPE-C PD协议 OTG - 开启充电与数据传输

TYPE-C PD协议&#xff0c;作为一种先进的充电与数据传输协议&#xff0c;正以其卓越的性能引领着充电与数据传输技术的发展。它通过USB Type-C接口&#xff0c;实现了充电与数据传输的完美结合&#xff0c;为用户带来了前所未有的便捷体验。 TYPE-C PD协议的一大亮点在于其支…

Go 项目依赖注入wire工具最佳实践介绍与使用

文章目录 一、引入二、控制反转与依赖注入三、为什么需要依赖注入工具3.1 示例3.2 依赖注入写法与非依赖注入写法 四、wire 工具介绍与安装4.1 wire 基本介绍4.2 安装 五、Wire 的基本使用5.1 前置代码准备5.2 使用 Wire 工具生成代码 六、Wire 核心技术5.1 抽象语法树分析5.2 …

KNN课堂(分类课堂(可用kd树/特征归一化提高精度)))

实验代码&#xff1a; # 导入所需要的库 import numpy as np import pandas as pd from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split from sklearn.neighbors import KNeighborsClassifier # 导入数据集 df pd.…

缓存雪崩以及解决思路

缓存雪崩&#xff1a;缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机&#xff0c;导致大量请求到达数据库&#xff0c;带来巨大压力。 解决方案&#xff1a; 给不同的Key的TTL添加随机值 利用Redis集群提高服务的可用性 给缓存业务添加降级限流策略 给业务…