Java 入门指南:Java NIO —— Selector(选择器)

news2024/11/16 4:28:39

NIO 的引入

在传统的 Java I/O 模型(BIO)中,I/O 操作是以阻塞的方式进行的。当一个线程执行一个 I/O 操作时,它会被阻塞直到操作完成。这种阻塞模型在处理多个并发连接时可能会导致性能瓶颈,因为需要为每个连接创建一个线程,而线程的创建和切换都是有开销的。

为了解决这个问题,在 Java1.4 版本引入了 NIO(New I/O or Non-Blocking I/O)java.nio。提供了一种基于缓冲区、选择器和非阻塞 IO 模型的 IO 处理方式。相比于之前的 BIO 模型,NIO 可以实现更高的并发、更低的延迟以及更少的资源消耗。

I/O 包和 NIO 已经很好地集成了,java.io 也已经以 NIO 为基础重新实现了,所以现在它可以利用 NIO 的一些特性。例如,java.io 包中的一些类包含以块的形式读写数据的方法,这使得即使在面向流的系统中,处理速度也会更快。

![[BIO vs NIO.png]]
Java NIO 概要介绍:初识 Java NIO

使用 NIO 并不一定意味着高性能,它的性能优势主要体现在高并发和高延迟的网络环境下。当连接数较少、并发程度较低或者网络传输速度较快时,NIO 的性能并不一定优于传统的 BIO 。

Selector

NIO 实现了 IO 多路复用中的 Reactor 模型,一个线程 Thread 使用一个选择器 Selector 通过轮询的方式去监听多个通道 Channel 上的事件,从而让一个线程就可以处理多个事件。

通过配置监听的通道 Channel 为非阻塞,那么当 Channel 上的 IO 事件还未到达时,就不会进入阻塞状态一直等待,而是继续轮询其它 Channel,找到 IO 事件已经到达的 Channel 执行。

由于创建和切换线程的开销很大,所以使用一个线程来处理多个事件具有更好的性能。

Selector 是 Java NIO(New I/O)库中的一个重要组件,它用于实现非阻塞 I/O 操作。它可以用于管理多个通道(如网络套接字或文件通道)的事件,从而使单个线程能够有效地处理多个通道的 I/O 操作。

使用 Selector,可以注册一个或多个通道(通道必须为非阻塞模式!),并指定感兴趣的事件类型,例如连接操作、读操作或写操作。然后,Selector 会监视这些通道上发生的事件,并且只有当感兴趣的事件发生时,才会通知我们。这样就可以在单个线程中同时处理多个通道的 I/O 事件,而无需为每个通道分配一个独立的线程。

常用方法

  1. open():打开一个选择器。

  2. select():选择一组 I/O 操作已经准备就绪的通道。该方法是阻塞的,直到至少有一个通道就绪,或者调用线程被中断。

  3. select(long timeout):选择一组 I/O 操作已经准备就绪的通道,但最多等待指定的超时时间(以毫秒为单位)。该方法是阻塞的,直到至少有一个通道就绪、超时时间到达或调用线程被中断。

  4. selectNow():选择一组 I/O 操作已经准备就绪的通道,但不会阻塞。如果没有任何通道就绪,该方法会立即返回0。

  5. selectedKeys():获取当前已经选择(就绪)进行 I/O 操作的通道的 SelectionKey 集合。

  6. wakeup():唤醒阻塞在 select()select(long timeout) 方法上的线程。

  7. keys():返回当前注册在监听器上的所有选键集合,即返回一个包含所有已经注册过的通道的 SelectionKey 集合。包括已经取消注册但还未从选键集合中移除的对象,因此需要进行有效性判断,例如使用 isValid() 先判断选键是否有效。

  8. close():关闭选择器。

SelectionKey

SelectionKeySelector 的注册对象,用于表示注册在 Selector 上的通道和感兴趣的事件。它是 NIO 中 Selector API 的核心之一。

在使用 Selector 进行事件驱动的网络编程时,每个注册到 Selector 上的通道都会关联一个 SelectionKey 对象。SelectionKey 维护了通道的状态以及感兴趣的事件。

使用 SelectionKey 可以实现基于事件驱动的处理模式,实现高效的并发网络编程。

事件类别
  • SelectionKey.OP_CONNECT:表示连接已经建立,适用于客户端的 SocketChannel。

  • SelectionKey.OP_ACCEPT:表示通道已经准备好接受新的连接请求,适用于服务端的 ServerSocketChannel。

  • SelectionKey.OP_READ:表示通道已经准备好进行读操作,即可以从通道中读取数据。

  • SelectionKey.OP_WRITE:表示通道已经准备好进行写操作,即可以向通道中写入数据。

它们在 SelectionKey 的定义如下:

public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;

每个事件可以被当成一个位域,从而组成事件集整数。例如:

int interestSet = SelectionKey.OP_READ | 
				 SelectionKey.OP_WRITE;
方法

SelectionKey 包含以下重要的属性和方法:

  1. channel():返回与此选择键关联的通道。

  2. selector():返回创建此 SelelctionKey 所属的选择器。

  3. interestOps():返回选择键当前感兴趣的操作集合,即注册时指定的操作集合。

  4. readyOps():返回通道当前已经准备就绪的操作集合。可以与interestOps() 方法的结果进行位运算判断具体的就绪事件类型。

  5. isAcceptable():判断通道是否已经准备好接受新的连接。

  6. isConnectable():判断通道是否已经准备好完成连接。

  7. isReadable():判断通道是否已经准备好进行读取操作。

  8. isWritable():判断通道是否已经准备好进行写入操作。

  9. attach(Object obj)attachment():用于在选择键上附加一个对象,以便在后续处理中获取或更新相关信息。

  10. cancel():取消该 SelectionKey 的注册,通道不再与 Selector 相关联。

  11. remove():移除指定的 SelectionKey 对象,以便下一次调用 select() 方法时不会再次触发该事件。

使用 SelectionKey 对象时,应注意它的生命周期和正确的使用方式,以避免出现资源泄漏或其他问题。可以通过选择键集合(在选择器上调用 selectedKeys() 方法)来获取就绪的选择键,并在处理完后进行适当的移除或取消注册操作。

使用流程
  1. 通过 ServerSocketChannelSocketChannelregister(Selector sel, int ops) 方法将通道注册到 Selector 上,返回一个 SelectionKey 对象。

  2. 可以通过 SelectionKey 对象获取通道、选择器、事件集合、选择键集合等信息。

  3. 通过 SelectorselectedKeys() 方法可以获取当前已经就绪的 SelectionKey 集合,可以遍历集合处理就绪事件。

注意事项
  • 一个通道只能注册到一个 Selector 上,且注册后会返回一个唯一的 SelectionKey

  • SelectionKey 的事件集合可以使用 interestOps(int ops) 方法进行更新,但更新后并不会立即生效,需要再次调用 Selectorselect() 方法。

  • 使用附件对象可以将自定义的数据与 SelectionKey 相关联,以便在事件处理时获取和使用。

  • 取消 SelectionKey 后,通道仍然保持打开状态,需要手动关闭。

使用流程

使用 Java NIO 中的选择器(Selector)时,通常可以遵循以下流程:

  1. 创建一个选择器:使用 Selector.open() 方法打开一个选择器对象。
Selector selector = Selector.open();
  1. 向选择器注册通道:通过调用通道的 register(Selector selector, int interestOps) 方法将通道注册到选择器上,指定对于该通道感兴趣的 I/O 事件类型(如读、写、连接等)。
ServerSocketChannel channel = ServerSocketChannel.open();
channel.bind(new InetSocketAddress("localhost",8888));
// 将通道设置为非阻塞模式
channel.configureBlocking(false);
channel.register(selector,Selelction.OP_ACCEPT);

一个选择器可以同时注册多个通道。

  1. 不断循环选择就绪的通道:在循环中使用选择器的 select() 方法或 select(long timeout) 方法等待通道就绪,并返回已经准备就绪的通道数量。可以根据返回值判断是否有通道就绪。
while(true){
	selector.select();
	// 对事件进行操作
	// ...
}
  1. 处理就绪的通道:通过调用选择器的 selectedKeys() 方法,获取已经准备就绪的通道的选择键集合。遍历选择键集合,可以使用 SelectionKey 对象来获取具体的就绪通道,以及就绪的I/O事件类型。
Set<SelelctionKey> keys =  selector.selectedKeys();
Iterator<SelectionKey> iter = keys.iterator();
while(iter.hasNext()){
	Selection key = iter.next();
	// 处理事件
	// ...
}
  1. 根据事件类型进行相应的业务处理:根据通道的可操作事件类型(读、写、连接等),使用相应的方法处理相应的业务逻辑,并可能对通道进行读写操作。
// 如果是连接事件,accept 并注册关注 OP_READ 事件
if(key.isAcceptable()){
	ServerSocketChannel server = (ServerSocketChannel)key.channel();
	SocketChannel client = server.accept();
	client.configureBlocking(false);
	client.register(selector,SelectionKey.OP_READ);
}

// 如果是读事件,读取数据并响应
else if(key.isReadable()){
	ServerSocket client = (ServerSocket)key.channel();
	ByteBuffer buffer = ByteBuffer.allocate(1024);
	client.read(buffer);
	buffer.flip();
	String request = new String(buffer.array(),
								0,
								buffer.limit(),
								StandardCharsets.UTF_8)
									.trim();
	System.out.println("Client request: " + request);
	String response = "Response from server: ";
	ByteBuffer outBuffer = ByteBuffer.wrap(response.getBytes());
	client.write(outBuffer);
}

//手动从集合中移除当前事件,避免重复处理
iter.remove();
  1. 取消通道的注册:在处理完就绪的通道后,如果不再关注该通道的 I/O 事件,可以调用选择键的 cancel() 方法取消通道的注册。
key.cancel();
  1. 处理其他操作(可选):根据具体需求,可能要处理一些其他操作,如再次注册通道、关闭选择器等。

  2. 关闭选择器:当不再需要使用选择器时,应调用选择器的 close() 方法关闭选择器

selector.close();

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

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

相关文章

随身wifi靠谱吗?适合哪类人使用?靠谱随身wifi怎么选?热门随身wifi推荐测评!

你真的适合用随身wifi吗&#xff1f; 户外工作者&#xff1a;外卖员&#xff0c;滴滴司机&#xff0c;卡车司机&#xff0c;户外直播等人群对于网络的稳定性和流量的需求还是比较高的。随身wifi便携&#xff0c;信号稳定&#xff0c;流量多性价比高的特点符合户外工作者对网络的…

制造企业看过来!这15款工程软件值得推荐!

本文将盘点15款工程软件&#xff0c;供企业选型参考。 工程软件就如同工程领域的得力助手&#xff0c;能让工程建设的各个环节都变得更加高效、精准。 对于工程企业来说&#xff0c;如果没有合适的工程软件&#xff0c;就像工匠没有趁手的工具&#xff0c;难以打造出精良的作品…

vmware典型安装centos

创建虚拟机 选择centos镜像 设置用户名

Oracle 网络安全产品安全认证检索

自2023年7月1日起&#xff0c;国家网信办、工业和信息化部、公安部、国家认证认可监督管理委员会统一公布和更新网络关键设备和网络安全专用产品清单。列入《网络关键设备和网络安全专用产品目录》的网络安全专用产品应当按照《信息安全技术网络安全专用产品安全技术要求》等相…

日本对COBOL的需求--一篇说清楚

关于COBOL&#xff08;Common Business-Oriented Language&#xff09;在中国和日本的使用情况&#xff0c;确实存在显著的差异。 在中国&#xff0c;COBOL被视为一种较早的编程语言&#xff0c;其使用范围和需求已经相对较小&#xff0c;这主要是由于技术更新和新一代编程语言…

【微信小程序入门】1、初识微信小程序

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

1. 【Java开发手册】| 前言

最早接触 《Java 开发手册》大概是 2020 年的时候&#xff0c;那个时候刚出** 嵩山版 **, 当时也就是大致扫了一遍&#xff0c;对于一些约定其实也不了解&#xff0c;并没有太在意。随着开发经验的积累&#xff0c;从当初埋头写业务的大头兵&#xff0c;到现在成为了一个带领小…

基于vue框架的餐馆管理系统jo0i7(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,菜品分类,菜品信息,餐馆介绍,后厨,菜品订单,后厨接单,完成订单 开题报告内容 基于Vue框架的餐馆管理系统开题报告 一、研究背景与意义 随着餐饮行业的蓬勃发展&#xff0c;餐馆面临着日益激烈的市场竞争和消费者多样化的需求。传…

安泰功率放大器应用领域:MEMS传感器的应用有哪些

功率放大器的应用领域很广泛&#xff0c;从超声测试、材料测试、水声测试再到压电驱动、电磁驱动生物医疗&#xff0c;它都能为整个系统提供强劲的激励&#xff0c;同样功率放大器在MEMS传感器系统的激励中也有着良好应用&#xff0c;今天Aigtek安泰电子就带大家走进MEMS传感器…

Elasticsearch 中,term 查询和 match 查询的区别

文章目录 前言Elasticsearch 中&#xff0c;term 查询和 match 查询的区别1. Term 查询2. Match 查询3. 总结 前言 如果您觉得有用的话&#xff0c;记得给博主点个赞&#xff0c;评论&#xff0c;收藏一键三连啊&#xff0c;写作不易啊^ _ ^。   而且听说点赞的人每天的运气都…

Ceruletide 雨蛙素;雨蛙肽;硫酸化蓝肽 简介

目录号 M9316 Ceruletide 雨蛙素&#xff1b;雨蛙肽&#xff1b;硫酸化蓝肽 Ceruletide (Caerulein) 是从澳大利亚青蛙皮肤中分离的生物活性十肽&#xff0c;是一种缩胆囊素受体 (cholecystokinin receptor) 激动剂。此外&#xff0c;Ceruletide还可用于构建小鼠急性胰腺炎模型…

羊大师:防疫不松懈:开学季儿童个人防护小贴士

随着秋风送爽&#xff0c;新学期的脚步悄然而至。为了让孩子们以最佳状态迎接知识的海洋&#xff0c;准备一份全面而实用的学习用品清单显得尤为重要。今天&#xff0c;我们就来一场“开学必备大放送”&#xff0c;帮助家长们轻松备战开学季&#xff01; 文具套装是基础中的基础…

Git —— 2、配置本地与远程免密仓库免密访问、实操创建本地仓库与上github新仓库关联

配置本地与远程仓库免密访问 1、在Git中生成本地密钥&#xff08;指令最后为你github登录账户&#xff09; ssh-keygen -t rsa -C wuyechuangdang163.com 2、将本地生成的"id_rsa.pub"内容拷贝到github网站新创建的ssh密钥中。 3、测试连通 ssh -T gitgithub.com &am…

Xilinx FPGA在线升级——升级思路

一、绪论 网上很多文章都讲述了Xilinx FPGA在线升级即回退的优势&#xff0c;在这里仅简述一遍。优势在于可不拆机的情况下改变FPGA的功能&#xff0c;可进行产品迭代。回退的优势是避免升级过程中一些突发情况导致板卡成为废板。至少Golden里面包含了可进行升级的部分代码。 …

错误提示:vcruntime140.dll丢失怎么办?有哪些办法可以将vcruntime140.dll修复

当你的应用程序突然崩溃或无法启动&#xff0c;并弹出一个含有“vcruntime140.dll缺失”错误提示的对话框时&#xff0c;这意味着你的系统中缺少一个关键的 DLL 文件。这种错误常见于安装了大量第三方软件和游戏的电脑上&#xff0c;尤其是在系统更新或软件卸载后。解决这一问题…

【node.js】基础之修改文件

node.js 基础(一) node.js是什么&#xff1f; 上面这句话的意思就是&#xff1a;Node.js 是一个开源的&#xff0c;跨平台的javascript运行环境。通俗的说就是一个应用程序或者说是一个软件&#xff0c;可以运行javascript。 Node.js的作用&#xff1a; 开发服务器应用。 将数…

移动端视频编辑SDK,智能识别,字幕自动生成

Vlog已成为人们分享生活、表达自我的重要方式&#xff0c;对于众多内容创作者而言&#xff0c;如何在视频中高效、精准地添加字幕&#xff0c;既提升观众体验&#xff0c;又节省宝贵时间&#xff0c;一直是一大挑战。美摄科技&#xff0c;作为视频编辑技术的先驱者&#xff0c;…

你知道吗?这些plm项目管理系统大厂项目经理都在用!

本文将盘点主流的plm项目管理系统&#xff0c;为企业选型提供参考 。 高效的plm项目管理系统是确保工程顺利进行、按时交付以及控制成本的关键&#xff0c;据美国建筑行业研究院的研究数据表明&#xff0c;实施高效项目管理的建筑企业&#xff0c;能够将项目成本降低 5%-10%。我…

【qt】qss使用

1.按钮设置颜色 ui->pushButton->setStyleSheet("QPushButton { color : red;}");也可以通过rgb来设置 ff表示红色拉满&#xff0c;gb为0当然是红色 这只是针对pushbutton对象的控件设置的&#xff0c;如果我想设置所有的按钮空间都是一个颜色 这是通过设置界…

关于OBI 在unity URP环境下使用的正确步骤

OBI在URP环境下使用的正确步骤 资料&#xff1a;OBI官方文档&#xff1a;Obi Physics for Unity - Big Picture 1、替换Shader 为 URP环境下&#xff1a; 2、渲染管线功能列表添加&#xff1a; ①.在Graphics配置中查找当前URP渲染管线 ②.添加Obi流体渲染功能项 ③、设URP环…