通俗易懂地讲解JAVA的BIO、NIO、AIO

理解Java的I/O模型(BIO、NIO、AIO)对于构建高性能网络应用至关重要

🧠 通俗理解:快递站的故事

想象一个快递站:
BIO:就像快递站为每一个包裹都安排一位专员。专员从接到包裹到处理完(签收、分拣、通知取件)之前,只能守着这个包裹,不能做别的事。包裹一多,专员就不够用了。

NIO:快递站只安排一位大堂经理。经理会定期轮询每个货架(“1号货架有新的包裹吗?2号呢?…”)。发现某个货架有包裹到达,就马上安排处理。一个人可以照看很多货架。

AIO:快递站装了一套智能系统。你只需告诉系统“包裹到了就叫我”,然后就可以去忙别的。系统会在包裹真正到达时主动回调通知你:“你的包裹到了,来处理吧”。你完全不需要轮询等待。

🔍 一、核心概念:阻塞/非阻塞 vs 同步/异步

在深入之前,先理解两组核心概念:

  1. 阻塞与非阻塞:关注的是线程的状态
    阻塞:调用一个方法,线程会一直等待,直到该方法返回结果。

    非阻塞:调用一个方法,线程立刻返回,不会傻等。你可以去做别的事。

  2. 同步与异步:关注的是消息通信的机制
    同步:调用一个方法后,需要调用者自己主动等待或不断询问结果。

    异步:调用一个方法后,调用者就去忙别的了。方法执行完毕后,会主动通知(回调)调用者。

I/O模型阻塞 vs 非阻塞同步 vs 异步通俗理解
BIO阻塞同步线程一直等,直到数据准备好
NIO非阻塞同步线程不断轮询,问数据好了没
AIO非阻塞异步线程发起请求就去干别的,系统好了会回调

𝟭. BIO (Blocking I/O) - 阻塞式I/O

原理

BIO是Java最早期的I/O模型,采用“一个连接对应一个线程” 的模式。当服务器接收到一个客户端连接时,就会创建一个新线程来处理该连接的所有读写操作。读写操作本身是阻塞的,意味着如果数据没有准备好,线程就会一直等待,什么也干不了。

代码示例
// 简化版的BIO服务器代码
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {Socket clientSocket = serverSocket.accept(); // (1) 阻塞点:等待客户端连接new Thread(() -> { // 为每个连接创建一个新线程InputStream in = clientSocket.getInputStream();in.read(); // (2) 阻塞点:等待客户端发送数据// ... 处理数据并响应}).start();
}

图示理解


客户端1 ──────> 线程1 (阻塞中...)
客户端2 ──────> 线程2 (阻塞中...)
客户端3 ──────> 线程3 (阻塞中...)
...

每个箭头都代表一个独立的线程,大部分线程都在空闲等待,浪费资源。

优缺点

优点:编程模型非常简单,容易理解和上手。

缺点:线程是昂贵的系统资源。每个线程都需要内存(默认约1MB线程栈)和CPU调度开销。当连接数暴增时,线程数也会线性增长,导致CPU忙于线程上下文切换,最终耗尽资源(线程爆炸),性能急剧下降。适用于连接数较少且固定的场景。

𝟮. NIO (Non-blocking I/O) - 非阻塞式I/O / 新I/O

为了解决BIO的问题,Java 1.4引入了NIO。其核心是一个线程可以处理多个连接,关键在于Selector(选择器)。

核心组件

NIO有三大核心概念

  1. Channel(通道):类似于BIO中的Stream,但它是双向的(可读可写),并且需要配合Buffer使用。
  2. Buffer(缓冲区):一个容器,所有数据都是通过Buffer来读写的。
  3. Selector(选择器):多路复用器。一个Selector可以同时轮询注册在它身上的多个Channel,检查哪些Channel已经做好了读写准备。这样,单个线程就可以管理多个Channel。
工作流程(核心)
  1. 将多个Channel(比如代表连接的SocketChannel)注册到Selector上,并告诉Selector你关心什么事件(如:连接就绪OP_ACCEPT、读就绪OP_READ)。
  2. 线程调用Selector.select()方法阻塞,等待事件发生。
  3. 当有事件(如某个Channel可读了)发生时,select()方法返回,并返回一个SelectionKey集合。
  4. 线程遍历这些Key,根据事件类型(可读、可写等)进行相应的处理。
代码示例
// NIO服务器代码核心结构
Selector selector = Selector.open(); // 创建Selector
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false); // 设置为非阻塞模式
serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册到Selector,关注ACCEPT事件while (true) {selector.select(); // (1) 阻塞,直到有事件发生Set<SelectionKey> selectedKeys = selector.selectedKeys();for (SelectionKey key : selectedKeys) {if (key.isAcceptable()) {// 处理新连接} else if (key.isReadable()) {// 处理读事件SocketChannel channel = (SocketChannel) key.channel();ByteBuffer buffer = ByteBuffer.allocate(1024);channel.read(buffer); // (2) 非阻塞读取:即使没数据也立即返回// ... 处理buffer中的数据}}selectedKeys.clear(); // 清理已处理的事件
}

图示理解

            ┌─────────┐
客户端1 ─────┤         ││         │
客户端2 ─────┤         ├───→ 线程 (运行Selector)│Selector │
客户端3 ─────┤         ││         │
客户端N ─────┤         │└─────────┘

一个Selector线程可以同时监听无数个Channel(客户端连接)。线程只在select()处阻塞,一旦有事件到来,它就高效地去处理那些就绪的Channel

优缺点

优点:极大地减少了线程数量,解决了BIO的线程爆炸问题,能够轻松管理数万甚至更多连接。

缺点:编程模型复杂。需要自己管理Buffer、Channel、Selector和事件状态机。对开发者的要求更高。本质上是同步非阻塞,因为线程仍需主动轮询(通过Selector)就绪的Channel。

𝟯. AIO (Asynchronous I/O) - 异步I/O

Java 7引入了AIO(又称NIO.2),它是真正的异步非阻塞I/O。

原理

AIO采用回调Future机制。用户线程发起一个I/O操作(如read)后,立即返回,不会阻塞。应用程序可以去处理其他任务。操作系统会在底层完成整个I/O操作(将数据从内核空间拷贝到用户空间),然后主动通知(回调)应用程序。

代码示例 (回调式)
// AIO服务器示例 (使用回调CompletionHandler)
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8080));// 开始异步接受连接,并传入一个CompletionHandler来处理接入的连接
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {@Overridepublic void completed(AsynchronousSocketChannel client, Void attachment) {// (1) 连接建立成功的回调方法ByteBuffer buffer = ByteBuffer.allocate(1024);// (2) 开始异步读操作,并传入另一个CompletionHandler来处理读完成事件client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer bytesRead, ByteBuffer buffer) {// (3) 数据读取完成的回调方法if (bytesRead > 0) {buffer.flip();// ... 处理数据}}@Overridepublic void failed(Throwable exc, ByteBuffer buffer) {// 处理读失败}});// 立即再次调用accept,准备接受下一个连接server.accept(null, this);}@Overridepublic void failed(Throwable exc, Void attachment) {// 处理接受连接失败}
});
// 主线程可以继续做其他事,或者直接休眠
Thread.sleep(Long.MAX_VALUE);

图示理解

用户线程: "系统,请帮我读数据"
操作系统: "好的"
用户线程: [去处理其他业务逻辑...]
...
操作系统: "数据读好了,这是结果,请你处理" (通过回调函数通知)整个过程由操作系统驱动,用户线程只需发起请求和接收结果。
优缺点

优点:理论上的性能王者。线程资源利用率极高,完全不会因为I/O而阻塞。

缺点
1. 严重依赖操作系统底层的异步I/O支持(如Linux的io_uring)。在Linux上,早期的AIO实现并不完善,因此应用不如NIO广泛。
2. 编程模型更复杂,回调嵌套可能导致“回调地狱”,代码可读性和维护性较差。
3. 生态和社区支持相对NIO(尤其是Netty)较弱。

📊 三者对比总结

特性BIO (阻塞I/O)NIO (非阻塞I/O)AIO (异步I/O)
核心模型一连接一线程单线程多连接(事件驱动)回调通知
阻塞与否阻塞非阻塞非阻塞
同步异步同步同步异步
线程利用率低(大量线程闲置阻塞)高(少量线程处理大量连接)极高(线程完全不阻塞)
编程复杂度低(简单直观)中高(需理解Selector/Buffer)高(回调地狱,异步编程)
吞吐性能低(受限于线程数)高(可应对高并发)理论最高
适用场景连接数少、快速开发高并发应用(网络服务器、IM)极高并发、底层依赖OS

💡 实践建议与选择

  1. BIO:在连接数非常少且对性能要求不高的教学示例或内部工具中可能见到。如果你的应用主要是​​低并发、长连接​​的大文件传输(例如,内部系统的文件备份、少量的用户上传),并且希望​​开发快速简单​​,那么​​BIO配合分块、断点续传等技术是完全可行的​​。
  2. NIO:目前事实上的主流和高性能网络编程的基石。虽然直接使用Java原生NIO API较复杂,但业界有非常成熟的Netty框架对其进行了极佳的封装和增强。绝大多数高性能网络应用(如Dubbo、RocketMQ、Elasticsearch)都基于Netty构建。
  3. AIO:由于平台支持度和编程模型的原因,直接使用AIO的场景较少。在Linux系统上,Netty等框架仍然优先选择基于NIO的模型。但在Windows(IOCP)或使用了最新io_uring的Linux系统上,AIO可能会有其用武之地。

简单来说:现在学网络编程,重点是理解NIO的原理,然后直接学习使用Netty框架。

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

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

相关文章

LabVIEW 泵轮检测系统

在汽车行业&#xff0c;泵轮作为液力变矩器关键部件&#xff0c;其质量检测极为重要。传统手工检测泵轮效率低且误差大&#xff0c;为此构建基于 LabVIEW 与西门子硬件结合的泵轮检测系统。 应用场景 聚焦汽车零部件生产车间&#xff0c;对泵轮总成进行出厂前检测。在液力变矩…

2025年8月月赛 T2 T3

一. 七天假日 T2原思路&#xff1a;直接计算左右括号的数量&#xff0c;然后直接输出他们的差改进思路&#xff1a; 用d值记录截止到当前位置&#xff0c;还需要多少个右括号可以满足非法要求cur&#xff1a;截止到当前位置&#xff0c;已经有多少个右括号sum是右括号位置的前缀…

数据结构----栈的顺序存储(顺序栈)

栈的特点&#xff1a;先进后出栈的操作&#xff1a;用数组进行存储&#xff08;1&#xff09;初始化&#xff1a;//栈 typedef struct {int *data;//指针模拟分配数组int top;//栈“顶”指针 }Stack; //初始化 Stack InitStack(){Stack s;//给数组分配空间s.data (int*)malloc…

React Hooks原理深度解析与高级应用模式

React Hooks原理深度解析与高级应用模式 引言 React Hooks自16.8版本引入以来&#xff0c;彻底改变了我们编写React组件的方式。然而&#xff0c;很多开发者仅仅停留在使用层面&#xff0c;对Hooks的实现原理和高级应用模式了解不深。本文将深入探讨Hooks的工作原理、自定义Hoo…

兼职网|基于SpringBoot和Vue的蜗牛兼职网(源码+数据库+文档)

项目介绍 : SpringbootMavenMybatis PlusVue Element UIMysql 开发的前后端分离的蜗牛兼职网&#xff0c;项目分为管理端和用户端和企业端。 项目演示: 基于SpringBoot和Vue的蜗牛兼职网 运行环境: 最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可…

TDengine 聚合函数 LEASTSQUARES 用户手册

LEASTSQUARES 函数用户手册 函数定义 LEASTSQUARES(expr, start_val, step_val)功能说明 LEASTSQUARES() 函数对指定列的数据进行最小二乘法线性拟合&#xff0c;返回拟合直线的斜率&#xff08;slope&#xff09;和截距&#xff08;intercept&#xff09;。该函数基于线性回…

Redis最佳实践——安全与稳定性保障之高可用架构详解

全面详解 Java 中 Redis 在电商应用的高可用架构设计一、高可用架构核心模型 1. 多层级高可用体系 #mermaid-svg-anJ3iQ0ymhr025Jn {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-anJ3iQ0ymhr025Jn .error-icon{fil…

ABAP 屏幕在自定义容器写多行文本框

文章目录变量定义容器等逻辑屏幕效果变量定义 CONSTANTS: GC_TEXT_LINE_LENGTH TYPE I VALUE 72. TYPES: TEXT_TABLE_TYPE(GC_TEXT_LINE_LENGTH) TYPE C OCCURS 0. DATA: GV_SPLITTER TYPE REF TO CL_GUI_EASY_SPLITTER_CONTAINER. DATA: GV_CUSTOM_CONTAINER TYPE REF TO CL_…

昆山精密机械公司8个Solidworks共用一台服务器

在当今高度信息化的制造业环境中&#xff0c;昆山精密机械公司面临着如何高效利用SolidWorks这一核心设计工具的现实挑战。随着企业规模的扩大和设计团队的分散&#xff0c;传统的单机授权模式已无法满足协同设计需求。通过引入云飞云共享云桌面解决方案&#xff0c;该公司成功…

【WebSocket✨】入门之旅(三):WebSocket 的实战应用

本篇文章将通过构建一个简单的实时聊天应用&#xff0c;演示如何在前端和后端搭建 WebSocket 系统&#xff0c;完成实时消息传输。通过实战&#xff0c;帮助你更好地理解 WebSocket 在实际项目中的应用。 目录 搭建 WebSocket 服务器WebSocket 客户端实现实时聊天应用示例常见…

CentOS 8-BClinux8.2更换为阿里云镜像源:保姆级教程

还在为 CentOS 8 官方源访问缓慢或不可用而烦恼吗&#xff1f;更换为国内镜像源&#xff0c;如阿里云&#xff0c;可以显著提升软件包下载速度和稳定性。本文将带你一步步完成 CentOS 8 镜像源的更换&#xff0c;让你的系统管理更顺畅。 准备工作 在进行任何系统配置更改之前…

MySQL中InnoDB索引使用与底层原理

MySQL Server端的缓存&#xff08;查询缓存&#xff09;是MySQL Server层的特性&#xff0c;而InnoDB的缓存&#xff08;缓冲池&#xff09;是InnoDB存储引擎层的特性。两者是完全独立的。下面我们来深入探讨这两者以及InnoDB索引的原理。1. MySQL Server层的缓存 - 查询缓存 (…

Python实战:实现监测抖音主播是否开播并录屏

实现这个功能,主要思路是循环检查主播状态 → 开播后获取直播流地址 → 使用FFmpeg录制。下面是一个基本的步骤表格: 步骤 关键行动 常用工具/库 1 获取主播直播间ID或唯一标识 浏览器开发者工具、抓包工具1 2 循环请求抖音API,查询主播直播状态 requests, time 3 解析API响…

init / record / required:让 C# 对象一次成型

标签&#xff1a; init record required with表达式 不可变性 数据模型 DTO 目录1. init 访问器&#xff1a;让不可变对象的创建更灵活1.1. 概念1.1.1. 语法1.1.2. 语义1.2. 设计初衷&#xff1a;解决什么问题&#xff1f;1.3. 使用方法1.3.1. 在对象初始化器中赋值&#xff08…

每天五分钟深度学习:神经网络的权重参数如何初始化

本文重点 在逻辑回归的时候,我们可以将神经网络的权重参数初始化为0(或者同样的值),但是如果我们将神经网络的权重参数初始化为0就会出问题,上节课程我们已经进行了简单的解释,那么既然初始化为0不行,神经网络该如何进行参数初始化呢?神经网络的权重参数初始化是模型训…

[论文阅读] 告别“数量为王”:双轨道会议模型+LS,破解AI时代学术交流困局

告别“数量为王”&#xff1a;双轨道会议模型LS&#xff0c;破解AI时代学术交流困局 论文信息信息类别具体内容论文原标题From Passive to Participatory: How Liberating Structures Can Revolutionize Our Conferences主要作者及机构1. Daniel Russo&#xff08;丹麦奥尔堡大…

趣味学solana(介绍)

你就是那个关键的“守门员”&#xff01; 为了方便理解Solana&#xff0c;我们把Solana 想象成一个巨大的、24小时不停歇的足球联赛。成千上万的足球运动员&#xff08;用户&#xff09;在不停地传球、射门&#xff08;发送交易&#xff09;&#xff0c;而整个比赛的结果必须被…

分布式事务性能优化:从故障现场到方案落地的实战手记(三)

第三部分&#xff1a;混合场景攻坚——从“单点优化”到“系统协同” 有些性能问题并非单一原因导致&#xff0c;而是锁竞争与事务耗时共同作用的结果。以下2个案例&#xff0c;展示综合性优化策略。 案例7&#xff1a;基金申购的“TCC性能陷阱”——从全量预留到增量确认 故障…

规则系统架构风格

考题 某公司拟开发一个VIP管理系统,系统需要根据不同商场活动,不定期更新VIP会员的审核标准和VIP折扣系统。针对上述需求,采用(__)架构风格最为合适。 A. 规则系统 B. 管道-过滤器风格 C. 事件驱动 D. 分层 一、什么是规则系统架构风格? 规则系统架构风格是一种将应…

kubeadm搭建生产环境的单master多node的k8s集群

k8s环境规划: podSubnet&#xff08;pod 网段&#xff09; 10.20.0.0/16 serviceSubnet&#xff08;service 网段&#xff09;: 10.10.0.0/16 实验环境规划: 操作系统&#xff1a;centos7.9 配置&#xff1a; 4G 内存/4核CPU/40G 硬盘 网络&#xff1a;NAT K8s集群角色ip主…