重新认识BIO、NIO、IO多路复用、Select、Poll、Epollo它们之间的关系

目录

一、背景

二、名词理解

(1)BIO

(2)NIO

(3)IO多路复用

(4)Select、Poll、Epollo

三、他们之间的关系总结

一、背景

最近又在学习网络IO相关知识,对我们常说的BIO、NIO、IO多路复用、Select、Poll、Epollo之间到底是什么关系,进行重新学习以及总结,特此记录一下。希望对一样迷惑的朋友也有一定的帮助

二、名词理解

(1)BIO

BIO它是Blocking IO的缩写,它的关键的特点就是阻塞IO,什么叫阻塞IO呢,就是你可以理解为它处理连接、读写事件是阻塞的,它采用阻塞方式进行数据读写操作。即当一个线程在执行IO操作时,若没有数据可读,则该线程会一直阻塞等待,直到有数据可读或者超时。如果还不理解我们不妨看看具体的BIO服务端实现的代码,加深自己的理解。

  • BIO服务端代码
package bio;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;/*** bio - server 类** @author chen* @date 2024年03月10日 10:43*/
public class BioServer {public static void main(String[] args) {try {System.out.println("*****服务端启动******");//定义一个ServerSocket对象进行一个服务端的接口注册ServerSocket serverSocket = new ServerSocket(8888);//监听客户端的Socket链接请求Socket socket = serverSocket.accept();//从socket对象中得到一个字节输入流对象InputStream is = socket.getInputStream();//将字节输入流包装成一个缓冲字符输入流(不能直接将字节输入流包装成缓冲字符输入流,先将字节输入流转成字符输入流,再转成缓冲字符输入流)BufferedReader br = new BufferedReader(new InputStreamReader(is));String msg;while ((msg = br.readLine()) != null){System.out.println("服务端收到信息:"+msg);}} catch (IOException e) {e.printStackTrace();}}}

这里我们看到BIO的accept和read方法都是阻塞方法,也就是说线程一旦调用就会进行阻塞

看到这里我们不妨可以思考一下,那我如果想实现多连接处理应该怎么实现?

既然我们一个连接一个线程,那是否我们可以看更多的线程去处理连接,没错,早期的BIO是这样都是这样去处理的,如下代码,加入线程池后的处理

package bio;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** TODO 类功能描述** @author chen* @date 2024年03月10日 10:43*/
public class BioMoreThreadServer {public static ExecutorService executorService = Executors.newFixedThreadPool(2);public static void main(String[] args) {try {System.out.println("*******服务端启动*********");//注册端口ServerSocket ss = new ServerSocket(8888);//定义一个死循环,负责不断的接收客户端的Socket的连接请求while (true){Socket socket = ss.accept();//创建一个独立的线程来处理这个客户端socket的通信需求
//                new ServerThreadReader(socket).start();//用线程池替代,线程池只是解决了防止频繁创建和销毁线程,并没有解决可以更多的支持连接这个问题(连接数还是有限)executorService.submit(new ServerThreadReader(socket));}} catch (IOException e) {e.printStackTrace();}}}

这里需注意一个细节,加入线程池后,并没有解决能够支持更多的连接的问题,仅仅只是防止线程频繁创建以及销毁,降低系统压力。

看完了BIO我们会提出一个疑问,那如果能够支持更大的连接数呢?

(2)NIO

 NIO是Java 1.4引入的新的IO模型,它采用了多路复用器(Selector)机制,通过少量线程同时管理多个通道,实现了单线程同时处理多个请求的效果。NIO具有高并发性、高吞吐量和更高的可靠性,适合处理连接数多且连接时间较短的场景。NIO的核心组件包括通道(Channel)、缓冲区(Buffer)和选择器(Selector)。选择器允许程序同时等待多个通道上的事件,如连接、数据到达等。

从我个人理解的角度,就是新的网络IO模型,增加了在网络IO读取过程中的一些特性,例如非阻塞、高吞吐、高并发性。那么他跟IO多路复用有什么关系呢?

(3)IO多路复用

IO多路复用(IO Multiplexing)一种同步IO模型,单个进程/线程就可以同时处理多个IO请求。一个进程/线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞应用程序,交出cpu。多路是指网络连接,复用指的是同一个进程/线程。

这里从我个人的角度理解,以盖房子为例,NIO是一个基础,提供了大量的基础工具(通道[Channel]、缓冲区[Buffer]和选择器[Selector]等等),就像一个我们要盖房子的各种工具已经给你备好了,接下来我们要盖一个怎样的房子(IO多路复用),相当于我们用NIO的技术实现了IO多路复用,如果没有NIO本身这项技术的支持,就不可能实现IO多路复用。

IO多路复用的代码实现

package nio;import java.io.IOException;
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.util.Iterator;
import java.util.Set;/*** TODO 类功能描述** @author chen* @date 2024年03月10日 13:01*/
public class NioServer {public static void main(String[] args) {try {ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);serverSocketChannel.bind(new InetSocketAddress(8888));Selector selector = Selector.open();serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);// 无限判断当前线程状态,如果没有中断,就一直执行while内容。while(!Thread.currentThread().isInterrupted()){// 获取准备就绪的channelif (selector.select() == 0) {continue;}// 获取到对应的 SelectionKey 对象Set<SelectionKey> keys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = keys.iterator();// 遍历所有的 SelectionKey 对象while (keyIterator.hasNext()){// 根据不同的SelectionKey事件类型进行相应的处理SelectionKey key = keyIterator.next();if (!key.isValid()){continue;}if (key.isAcceptable()){accept(serverSocketChannel,selector,key);}if(key.isReadable()){read(key);}// 移除当前的keykeyIterator.remove();}}} catch (Exception e) {e.printStackTrace();}}private static void read(SelectionKey key) {try {SocketChannel socketChannel = (SocketChannel) key.channel();//清除缓冲区,准备接受新数据ByteBuffer readBuffer = ByteBuffer.allocate(1024);//调整缓冲区大小为1024字节int numRead = socketChannel.read(readBuffer);;String str = new String(readBuffer.array(),0,numRead);System.out.println("read String is: " + str);}catch (Exception e){e.printStackTrace();}}private static void accept(ServerSocketChannel serverSocketChannel, Selector selector, SelectionKey key) {try {SocketChannel socketChannel = serverSocketChannel.accept();socketChannel.configureBlocking(false);// 注册客户端读取事件到selectorsocketChannel.register(selector, SelectionKey.OP_READ);System.out.println("client connected " + socketChannel.getRemoteAddress());}catch (Exception e){e.printStackTrace();}}
}

那这个时候IO多路复用算是明白了 ~~

那此时有提出一个疑问:那么IO多路复用跟我们常说的Select、Poll、Epollo又有什么关系?

(4)Select、Poll、Epollo

Select、Poll、Epollo的概念我这里就不一一叙说

总结起来他们就是操作系统函数,不同的操作系统(window、linux)有不同的实现,而且这项技术是不断演化,从早期的Select、演化到Poll、再到Epollo。上面讲到IO多路复用能够使用一个线程监听连接、读写事件,它们在操作系统底层是如何实现的呢?(任何的Java逻辑都是会回归操作系统,例如Java的创建线程,底层也是调用操作系统函数进行线程的创建)其实我们可以追到Select方法的源码,最终会调用本地方法,这里答案也就揭晓了,这三个操作系统函数,是操作系统帮我实现能够使用一个线程监听多个事件的功能,它们是实现IO多路复用的在操作系统层面的基石。

protected int doSelect(long var1) throws IOException {if (this.channelArray == null) {throw new ClosedSelectorException();} else {this.timeout = var1;this.processDeregisterQueue();if (this.interruptTriggered) {this.resetWakeupSocket();return 0;} else {this.adjustThreadsCount();this.finishLock.reset();this.startLock.startThreads();try {this.begin();try {this.subSelector.poll();} catch (IOException var7) {this.finishLock.setException(var7);}if (this.threads.size() > 0) {this.finishLock.waitForHelperThreads();}} finally {this.end();}this.finishLock.checkForException();this.processDeregisterQueue();int var3 = this.updateSelectedKeys();this.resetWakeupSocket();return var3;}}}

三、他们之间的关系总结

最后我画一张图来总结它们的关系

NIO本身这项技术本身是一个大的基石,我们利用了这个基石实现了IO多路复用,而Select、Poll、Epollo这些操作系统函数,是我们操作系统层面的基石,借助这种机制能够帮我们实现事件监听,实现单个线程处理多个事件。从而实现IO多路复用机制,这样就解决了BIO连接数的限制,能够支持处理更多的连接。

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

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

相关文章

linuxOPS基础_linux命令合集

uname查看操作系统信息 命令&#xff1a;uname [参数] 作用&#xff1a;获取计算机操作系统相关信息 参数&#xff1a;-a&#xff0c;选项-a代表all&#xff0c;表示获取全部的系统信息&#xff08;类型、全部主机名、内核版本、发布时间、开源计划&#xff09; 用法一&…

19. UE5 RPG使用GameplayEffect的Attribute Based Modifiers

前几篇文章我也说了GE的基础使用&#xff0c;但是&#xff0c;对一些属性的应用没有述说&#xff0c;后续&#xff0c;我将一点一点的将它们如何使用书写下来。 这一篇&#xff0c;主要就讲解一下Attribute Based Modifiers使用&#xff0c;先说一下它的应用场景&#xff0c;一…

9.用FFmpeg测试H.264文件的解码时间

1. Essence of Method 要测试对H.264文件的解码时间&#xff0c;可以使用FFmpeg进行操作。FFmpeg是一个开源的多媒体处理工具&#xff0c;可以用来处理视频和音频文件&#xff0c;包括解码H.264文件。以下是使用FFmpeg的命令行来测试解码时间的方法&#xff1a; ffmpeg -i in…

说说JVM的垃圾回收机制

简介 垃圾回收机制英文为Garbage Collection, 所以我们常常称之为GC。那么为什么我们需要垃圾回收机制呢&#xff1f;如果大家有了解过Java虚拟机运行时区域的组成(JVM运行时存在&#xff0c;本地方法栈&#xff0c;虚拟机方法栈&#xff0c;程序计数器&#xff0c;堆&#xf…

MLC-LLM框架的安卓应用部署实战

这几天根据官网教程把MLC-LLM在安卓端部署了一下&#xff0c;中间遇到了不少问题&#xff0c;也搜集了不少解决方案&#xff0c;同时也结合了别人的实践经历&#xff0c;现分享总结如下。 感谢博主tao_spyker的文章基于MLC LLM将Llama2-7B模型部署至Android手机运行&#xff0c…

程序人生——Java异常使用建议

目录 引出异常建议110&#xff1a;提倡异常封装&#xff1b;建议111&#xff1a;采用异常链传递异常 建议112&#xff1a;受检异常尽可能转化为非受检异常建议113&#xff1a;不要在finally块中处理返回值 建议114&#xff1a;不要在构造函数中抛异常建议115&#xff1a;使用Th…

【Exception系列】SocketTimeoutException

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

学习vue3第五节(reactive 及其相关)

1、定义 reactive() 创建一个响应式代理对象&#xff0c;不同于ref()可以创建任意类型的数据&#xff0c;而reactive()只能是对象&#xff0c;会响应式的深层次解包任何属性&#xff0c;将其标注为响应式 响应式是基于ES6的proxy实现的代理对象&#xff0c;该proxy对象与原对象…

单片机第四季-第一课:RTOS

1&#xff0c;RTOS来龙去脉 操作系统是什么&#xff1f; 以人类社会类比&#xff0c;小公司三四个人都是干活的&#xff0c;大公司有几万人其中有几千人从事管理工作&#xff0c;他们的工作是让其他人的干活效率更高。 51单片机为什么没有操作系统&#xff0c;因为51的性能太…

人脸检测的5种实现方法

众所周知&#xff0c;人脸识别是计算机视觉应用的一个重大领域&#xff0c;在学习人脸识别之前&#xff0c;我们先来简单学习下人脸检测的几种用法。 常见的人脸检测方法大致有5种&#xff0c;Haar、Hog、CNN、SSD、MTCNN&#xff1a; 相关构造检测器的文件&#xff1a;opencv…

服务器机器学习环境搭建(包括AanConda的安装和Pytorch的安装)

服务器机器学习环境搭建 1 服务器与用户 在学校中&#xff0c;我们在学校中是以用户的身份进行访问学校的服务器的。整体框架大致如下&#xff1a; 我们与root用户共享服务器的一些资源&#xff0c;比如显卡驱动&#xff0c;Cuda以及一些其他的公共软件。 一般情况下&#…

力扣新思路题:字符串轮转

非常简单的思路&#xff1a;将两个字符串s1接起来&#xff0c;并判断s2字符串是否是加长版s1字符串的子串 bool isFlipedString(char* s1, char* s2){if (strlen(s1) ! strlen(s2)) {return false;}int len strlen(s1);int i 0;char* arr (char*)malloc(sizeof(char) * len…

Netty之@Sharable注解

1.背景 熟悉Netty的人都了解&#xff0c;netty为每个连接的客户端都会创建一个channel用于通信相关。每个channel都绑定到一条channelpipeline上。而channelpipeline里由一个一个的节点channelhandler组成&#xff08;严格来说是channelhandlercontext&#xff09;。如果为所有…

【蓝桥杯选拔赛真题38】C++判断数字 第十四届蓝桥杯青少年创意编程大赛 算法思维 C++编程选拔赛真题解析

目录 C判断数字 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、推荐资料 C判断数字 第十四届蓝桥杯青少年创意编程大赛C选拔赛真题 一、题目要求 1、编程实现 给定一个正整数N(100≤N<100000)…

nginx实时流量拷贝ngx_http_mirror_module

参考&#xff1a; Module ngx_http_mirror_module Nginx流量拷贝ngx_http_mirror_module模块使用方法详解 ngx_http_mirror_module用于实时流量拷贝 请求一个接口&#xff0c;想实时拷贝这个请求转发到自己的服务上&#xff0c;可以使用ngx_http_mirror_module模块。 官网好像…

(done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW

一个视频&#xff1a;https://www.bilibili.com/video/BV1mb4y1y7EB/?spm_id_from333.337.search-card.all.click&vd_source7a1a0bc74158c6993c7355c5490fc600 这里有个视频&#xff0c;讲解得更加生动形象一些 总得来说&#xff0c;词袋模型(Bow, bag-of-words) 是最简…

PHP+golang开源办公系统CRM管理系统

基于ThinkPHP6 Layui MySQL的企业办公系统。集成系统设置、人事管理、消息管理、审批管理、日常办公、客户管理、合同管理、项目管理、财务管理、电销接口集成、在线签章等模块。系统简约&#xff0c;易于功能扩展&#xff0c;方便二次开发。 服务器运行环境要求 PHP > 7.…

手搭手RocketMQ发送消息

消息中间件的对比 消息中间件 ActiveMQ RabbitMQ RocketMQ kafka 开发语言 java erlang java scala 单击吞吐量 万级 万级 10万级 10万级 时效性 ms us ms ms 可用性 高(主从架构) 高(主从架构) 非常高(主从架构) 非常高(主从架构) 消息中间件: acti…

LabVIEW湍流等离子体束热效率优化

LabVIEW湍流等离子体束热效率优化 利用LabVIEW虚拟仪器技术&#xff0c;对湍流等离子体束的热效率进行了实时监测与优化&#xff0c;提高其在材料处理领域的应用效率和精度。通过双进气湍流等离子体发生器&#xff0c;实现了在不同工作参数下对热效率的实时在线监测&#xff0…

Luckysheet + Exceljs:H5实现Excel在线编辑、导入、导出及上传服务器的示例代码(完整版demo)

创建xeditor.html <!DOCTYPE html> <html><head><meta charset"UTF-8" /><title>Hello World!</title><!-- <link relstylesheet href./luckysheet/plugins/css/pluginsCss.css /><link relstylesheet href./luck…