【JavaSE】IO模型
IO,英文全称是 Input/Output,翻译过来就是输入/输出。我们听得挺多,就是磁盘 IO,网络 IO 等。IO 即输入/输出,到底谁是输入?谁是输出?IO 如果脱离了主体,会让人疑惑。
计算机角度的 IO
我们常说的输入输出,比较直观的意思就是计算机的输入输出,计算机就是主体。计算机分成分为 5 个部分:运算器、控制器、存储器、输入设备、输出设备。输入设备是向计算机输入数据和信息的设备,键盘,鼠标都属于输入设备;输出设备是计算机硬件系统的终端设备,用于接收计算机数据的输出显示,一般显示器、打印机属于输出设备。
操作系统角度的 IO
我们要将内存中的数据写入到磁盘的话,那么主体就是一个程序。操作系统负责计算机的资源管理和进程的调度,我们电脑上跑着的应用程序,其实是需要经过操作系统,才能做一些特殊操作,如磁盘文件读写、内存的读写等等。
真正的 IO 是在操作系统执行的。即应用程序的 IO 操作分为两种动作:IO 调用和 IO 执行。IO 调用是由进程(应用程序的运行态)发起,而 IO 执行是操作系统内核的工作。
应用程序发起的一次 IO 操作包含两个阶段:
IO 调用:应用程序进程向操作系统内核发起调用。
IO 执行:操作系统内核完成 IO 操作。
IO 模型
阻塞 IO
假设应用程序的进程发起 IO 调用,但是如果内核的数据还没准备好的话,那应用程序进程就一直在阻塞等待,一直等到内核数据准备好了,从内核拷贝到用户空间,才返回成功提示,此次 IO 操作,称之为阻塞 IO。
阻塞 IO 比较经典的应用就是阻塞 socket、Java BIO。阻塞 IO 的缺点就是:如果内核数据一直没准备好,那用户进程将一直阻塞,浪费性能,可以使用非阻塞 IO 优化。
非阻塞 IO
如果内核数据还没准备好,可以先返回错误信息给用户进程,让它不需要等待,而是通过轮询的方式再来请求,这就是非阻塞 IO。
非阻塞 IO 的流程如下:
1.应用进程向操作系统内核,发起 recvfrom( )读取数据。
2.操作系统内核数据没有准备好,立即返回 EWOULDBLOCK 错误码。
3. 应用程序进程轮询调用,继续向操作系统内核发起 recvfrom 读取数据。
4.操作系统内核数据准备好了,从内核缓冲区拷贝到用户空间。
5.完成调用,返回成功提示。
recvfrom() 用来接收远程主机经指定的 socket 传来的数据,并把数据传到由参数 buf 指向的内存空间。非阻塞 IO 模型,简称 NIO,Non-Blocking IO。它相对于阻塞 IO,虽然大幅提升了性能,但是它依然存在性能问题,即频繁的轮询,导致频繁的系统调用,同样会消耗大量的 CPU 资源。
IO 多路复用
既然 NIO 无效的轮询会导致 CPU 资源消耗,我们等到内核数据准备好了,主动通知应用进程再去进行系统调用。
IO 复用模型核心思路:系统给我们提供一类函数(如 select、poll、epoll),它们可以同时监控多个 fd 的操作,任何一个返回内核数据就绪,应用进程再发起 recvfrom() 系统调用。
文件描述符 fd(File Descriptor),它是计算机科学中的一个术语,形式上是一个非负整数。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符,用来表示相关文件信息。
select
应用进程通过调用 select 函数,可以同时监控多个 fd,在 select 函数监控的 fd中,只要有任何一个数据状态准备就绪了,select 函数就会返回可读状态,这时应用进程再发起 recvfrom( )请求去读取数据。select 的 IO 多路复用模型,只需要发起一次询问就够了,大大优化了性能。
但是,select 有几个缺点:监听的 IO 最大连接数有限,在 Linux 系统上一般为 1024kb。select 函数返回后,是通过遍历 fdset,找到就绪的描述符 fd(仅知道有 I/O 事件发生,却不知是哪几个 FD,所以遍历所有 FD)。因为存在大量遍历,所以会有连接数限制,所以后来又提出了 poll。与 select 相比,poll 解决了连接数限制问题。但是,select 和 poll 一样,还是需要通过遍历 FD 来获取已经就绪的 socket。如果同时连接大量客户端,在一时刻可能只有极少处于就绪状态,伴随着监视的描述符数量的增长,效率也会线性下降。因此经典的多路复用模型 epoll 诞生。
epoll
为了解决 select/poll 存在的问题,多路复用模型 epoll 诞生,它采用事件驱动来实现。epoll 先通过 epoll_ctl() 来注册一个 fd,一旦基于某个 fd 就绪时,内核会采用回调机制,迅速激活这个 fd,当进程调用 epoll_wait()时便得到通知。这里去掉了遍历文件描述符的坑爹操作,而是采用监听事件回调的机制。这就是 epoll的亮点。
select、poll、epoll 的区别
select | poll | epoll | |
---|---|---|---|
底层数据结构 | 数组 | 链表 | 红黑树和双链表 |
获取就绪的 fd | 遍历 | 遍历 | 事件回调 |
事件复杂度 | O(n) | O(n) | O(1) |
最大连接数 | 1024 | 无限制 | 无限制 |
fd 数据拷贝 | 每次调用 select,需要将 fd 数据从用户空间拷贝到内核空间 | 每次调用 poll,需要将 fd 数据从用户空间拷贝到内核空间 | 使用内存映射(mmap),不需要从用户空间频繁拷贝 fd 数据到内核空间 |
异步 IO
前面讲的 BIO,NIO,在数据从内核复制到应用缓冲的时候,都是阻塞的,因此 AIO 实现了 IO 全流程的非阻塞,就是应用进程发出系统调用后,是立即返回的,但是立即返回的不是处理结果,而是表示类似提交成功的意思。等内核数据准备好,将数据拷贝到用户进程缓冲区,发送信号通知用户进程 IO 操作执行完毕。
异步 IO 的优化思路很简单,只需要向内核发送一次请求,就可以完成数据状态询问和数据拷贝的所有操作,并且不用阻塞等待结果。日常开发中,有类似思想的业务场景:比如发起一笔批量转账,但是批量转账理比较耗时,这时候后端可以先告知前端转账提交成功,等到结果处理完,再通知前端结果即可。
一个生活中经典例子:
BIO
小明去吃饭,就这样在那里排队,等了一小时,轮到她了,然后才开始吃饭。
package IO;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;public class BIODemo1 {public static void main(String[] args) throws IOException, InterruptedException {ServerSocket serverSocket = new ServerSocket(9999, 1024);System.out.println("服务器启动");while (true) {Socket socket = serverSocket.accept();}}}
package IO;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class BIODemo2 {public static void main(String[] args) throws IOException, InterruptedException {//创建线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(50,50, 200,TimeUnit.MILLISECONDS,new ArrayBlockingQueue<>(20),Executors.defaultThreadFactory(),new ThreadPoolExecutor.CallerRunsPolicy());ServerSocket serverSocket = new ServerSocket(9999, 1024);System.out.println("服务器启动");while (true) {Socket socket = serverSocket.accept();System.out.println(socket + "连接到服务器");executor.execute(() -> {//执行任务});}}}
package IO;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;/*** BIO模拟NIO*/
public class BIODemo3 {public static void main(String[] args) throws IOException, InterruptedException {List<Socket> list = new ArrayList<>();ServerSocket serverSocket = new ServerSocket(9999, 1024);System.out.println("服务器启动");while (true) {// 设置非阻塞 serverSocket.set不阻塞Socket socket = serverSocket.accept(); // 不管接到接不到继续向下执行// 判断有没有接到if (socket != null) { // 没有人连接到服务器// 虽然没有人连接,但是有可能有人发送消息,此时也需要处理.// 循环集合中的socket} else { // 有人连接到服务器System.out.println(socket + "连接到服务器");list.add(socket);// 循环集合中的socket}}}}
NIO
小红也去吃饭,她一看要等挺久的,于是去逛会商场,每次逛一下,就跑回来看看,是不是轮到她了。于是最后她既购了物,又吃上饭了。
package IO;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;public class NIODemo1 {public static void main(String[] args) throws IOException {FileInputStream in = new FileInputStream("E:/source.txt");FileOutputStream out = new FileOutputStream("E:/dest.txt");FileChannel inchannel = in.getChannel();FileChannel outchannel = out.getChannel();ByteBuffer byteBuffer = ByteBuffer.allocate(1024);while (inchannel.read(byteBuffer) != -1) {byteBuffer.flip();outchannel.write(byteBuffer);byteBuffer.clear();}}}
package IO;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.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Set;/*NIO完整写法*/
public class NIODemo2 {public static void main(String[] args) throws InterruptedException, IOException {List<SocketChannel> list = new ArrayList<>();ServerSocketChannel serverSogketChannel = ServerSocketChannel.open();//创建服务器serverSogketChannel.bind(new InetSocketAddress(9999), 1024);serverSogketChannel.configureBlocking(false);//设置非阻塞//注册选择器Selector selector = Selector.open();//创建选择器serverSogketChannel.register(selector, SelectionKey.OP_ACCEPT);//向selector注册管道System.out.println("启动服务器");for (; ; ) {selector.select();Set<SelectionKey> selectionKeys = selector.selectedKeys();//返回所有选择器接收到的操作Iterator<SelectionKey> iterator = selectionKeys.iterator();while (iterator.hasNext()) {SelectionKey selectionKey = iterator.next();if (selectionKey.isAcceptable()) {//连接ServerSocketChannel serverSockChannel = (ServerSocketChannel) selectionKey.channel();SocketChannel acceptSocketChannel = serverSockChannel.accept();System.out.println(acceptSocketChannel.getRemoteAddress());acceptSocketChannel.configureBlocking(false);acceptSocketChannel.register(selector, SelectionKey.OP_WRITE);}if (selectionKey.isWritable()) {//写SocketChannel socketChannel = (SocketChannel) selectionKey.channel();String resp = "响应";try {Thread.sleep(500);socketChannel.write(ByteBuffer.wrap(resp.getBytes()));} catch (Exception e) {e.printStackTrace();}}if (selectionKey.isReadable()) {//读SocketChannel channel = (SocketChannel) selectionKey.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);int length = channel.read(buffer);String msg = "server receive msg:" + new String(buffer.array(), 0, length);System.out.println(msg);}iterator.remove();}}}
}
AIO
小华一样,去吃饭,由于他是高级会员,所以店长说,你去商场随便逛会吧,等下有位置,我立马打电话给你。于是小华不用干巴巴坐着等,也不用每过一会儿就跑回来看有没有等到,最后也吃上了饭。
相关文章:
【JavaSE】IO模型
IO,英文全称是 Input/Output,翻译过来就是输入/输出。我们听得挺多,就是磁盘 IO,网络 IO 等。IO 即输入/输出,到底谁是输入?谁是输出?IO 如果脱离了主体,会让人疑惑。 计算机角度的…...

手术缝合线合格品检测项目众多 线径又是其重要一环!
手术缝合线的合格与否,关系着使用及恢复情况,其品质的优劣非常重要,而要想得到合格的手术缝合线,则需要多种类型的仪器进行检测。其中线径就是重要一环,下面来看看线径检测仪,并简单介绍一下其他所需检测信…...

STM32 单片机最小系统全解析
STM32 单片机最小系统全解析 本文详细介绍了 STM32 单片机最小系统,包括其各个组成部分及设计要点与注意事项。STM32 最小系统在嵌入式开发中至关重要,由电源、时钟、复位、调试接口和启动电路等组成。 在电源电路方面,采用 3.3V 直流电源供…...
深度解析ElasticSearch:构建高效搜索与分析的基石原创
引言 在数据爆炸的时代,如何快速、准确地从海量数据中检索出有价值的信息成为了企业面临的重要挑战。ElasticSearch,作为一款基于Lucene的开源分布式搜索和分析引擎,凭借其强大的实时搜索、分析和扩展能力,成为了众多企业的首选。…...

【Python常用模块】_PyMySQL模块详解
课 程 推 荐我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)教程合集 👈👈…...

【算法思想·二叉树】最近公共祖先问题
本文参考labuladong算法笔记[拓展:最近公共祖先系列解题框架 | labuladong 的算法笔记] 0、引言 如果说笔试的时候经常遇到各种动归回溯这类稍有难度的题目,那么面试会倾向于一些比较经典的问题,难度不算大,而且也比较实用。 本…...

如何合并pdf文件,四款软件,三步搞定!
在数字化办公的浪潮中,PDF文档因其跨平台兼容性和安全性,成为了我们日常工作中不可或缺的一部分。然而,面对多个PDF文件需要整合成一个文件时,不少小伙伴可能会感到头疼。别担心,今天我们就来揭秘四款高效PDF合并软件&…...

仪表放大器AD620
AD623 是一款低功耗、高精度的仪表放大器,而不是轨到轨运算放大器。它的输入电压范围并不覆盖整个电源电压(轨到轨),但在单电源供电下可以处理接近地电位的输入信号。 AD620 和 AD623 都是仪表放大器,但它们在一些关键…...

【Qt网络编程】Tcp多线程并发服务器和客户端通信
目录 一、编写思路 1、服务器 (1)总体思路widget.c(主线程) (2)详细流程widget.c(主线程) (1)总体思路chat_thread.c(处理聊天逻辑线程&…...
SkyWalking 简介
SkyWalking是什么 skywalking是一个国产开源框架,2015年由吴晟开源 , 2017年加入Apache孵化器。skywalking是分布式系统的应用 程序性能监视工具,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。它是一款优秀的 APM(Application Performance Manag…...

语音合成(自然、非自然)
1.环境 Python 3.10.14 2.完成代码 2.1简陋版 import pyttsx3# 初始化tts引擎 engine pyttsx3.init()# 设置语音速度 rate engine.getProperty(rate) engine.setProperty(rate, rate - 50)# 设置语音音量 volume engine.getProperty(volume) engine.setProperty(volume, …...

redis简单使用与安装
redis redis 是什么 Redis 是一个开源的,使用 C 语言编写的,支持网络交互的,内存中的Key-Value 数据结构存储系统,支持多种语言,它可以用作数据库、缓存和消息中间件。 一、存储系统特性 内存存储与持久化 Redis 主要将数据存储在内存中,这…...
封装 WBXpopup 组件
这是Popup组件基于微博小程序,需要改变标签,以及一写方法 支持四个方向抽屉,以及中间弹出功能 // 用法 <template><wbx-view style"height: 100vh;"><!-- 对话框组件 --><wbx-view><wbx-text click&quo…...

【OJ刷题】双指针问题6
这里是阿川的博客,祝您变得更强 ✨ 个人主页:在线OJ的阿川 💖文章专栏:OJ刷题入门到进阶 🌏代码仓库: 写在开头 现在您看到的是我的结论或想法,但在这背后凝结了大量的思考、经验和讨论 目录 1…...

详解:Tensorflow、Pytorch、Keras(搭建自己的深度学习网络)
这是一个专门对Tensorflow、Pytorch、Keras三个主流DL框架的一个详解和对比分析 一、何为深度学习框架? 你可以理解为一个工具帮你构建一个深度学习网络,调用里面的各种方法就能自行构建任意层,diy你想要的DNN,而且任意指定学习…...

【CSS in Depth 2 精译_035】5.5 Grid 网格布局中的子网格布局(全新内容)
当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一章 层叠、优先级与继承(已完结) 1.1 层叠1.2 继承1.3 特殊值1.4 简写属性1.5 CSS 渐进式增强技术1.6 本章小结 第二章 相对单位(已完结) 2.1 相对…...
Java是怎么处理死锁的
文章目录 避免死锁避免嵌套锁资源进行排序超时锁 检测死锁通过Java提供的API检查死锁情况jStack监控工具 Java 本身没有内置的机制自动处理死锁问题,但可以采取一些策略和技术来检测和避免死锁。 避免死锁 避免嵌套锁 尽可能减少嵌套锁操作,避免在一个…...
Effective Java 学习笔记 方法签名设计
目录 谨慎选择方法名称 不要过于追求提供便利的快捷方法 避免过长的参数列表 对于参数类型优先使用接口而不是类 对于boolean参数,要优先使用两个元素的枚举类型 本文接续前一篇文章聚焦Java方法签名的设计,方法签名包括了方法的输入和输出参数以及…...
毛利超70%、超70+智驾客户,这家AI数据训练服务商刚刚止亏
AI训练数据服务第一股海天瑞声终于迎来了“曙光”。 日前,海天瑞声发布2024年半年报显示,上半年其实现营收9242.63万,同比增长24.13%;实现净利润41.64 万元,不过同比去年同期的亏损1724.14万元,扭亏为盈。…...

本地部署高颜值某抑云音乐播放器Splayer并实现无公网IP远程听歌
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...

23-Oracle 23 ai 区块链表(Blockchain Table)
小伙伴有没有在金融强合规的领域中遇见,必须要保持数据不可变,管理员都无法修改和留痕的要求。比如医疗的电子病历中,影像检查检验结果不可篡改行的,药品追溯过程中数据只可插入无法删除的特性需求;登录日志、修改日志…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...

ABAP设计模式之---“简单设计原则(Simple Design)”
“Simple Design”(简单设计)是软件开发中的一个重要理念,倡导以最简单的方式实现软件功能,以确保代码清晰易懂、易维护,并在项目需求变化时能够快速适应。 其核心目标是避免复杂和过度设计,遵循“让事情保…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf
FTP 客服管理系统 实现kefu123登录,不允许匿名访问,kefu只能访问/data/kefu目录,不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

MFC 抛体运动模拟:常见问题解决与界面美化
在 MFC 中开发抛体运动模拟程序时,我们常遇到 轨迹残留、无效刷新、视觉单调、物理逻辑瑕疵 等问题。本文将针对这些痛点,详细解析原因并提供解决方案,同时兼顾界面美化,让模拟效果更专业、更高效。 问题一:历史轨迹与小球残影残留 现象 小球运动后,历史位置的 “残影”…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
2025年低延迟业务DDoS防护全攻略:高可用架构与实战方案
一、延迟敏感行业面临的DDoS攻击新挑战 2025年,金融交易、实时竞技游戏、工业物联网等低延迟业务成为DDoS攻击的首要目标。攻击呈现三大特征: AI驱动的自适应攻击:攻击流量模拟真实用户行为,差异率低至0.5%,传统规则引…...

链式法则中 复合函数的推导路径 多变量“信息传递路径”
非常好,我们将之前关于偏导数链式法则中不能“约掉”偏导符号的问题,统一使用 二重复合函数: z f ( u ( x , y ) , v ( x , y ) ) \boxed{z f(u(x,y),\ v(x,y))} zf(u(x,y), v(x,y)) 来全面说明。我们会展示其全微分形式(偏导…...