Java I/O模型深度解析:BIO、NIO、AIO的区别与联系

引言

在Java的网络编程与文件操作中,I/O(输入/输出)模型是绕不开的核心话题。从早期的BIO(Blocking I/O)到Java 1.4引入的NIO(Non-blocking I/O),再到Java 7推出的AIO(Asynchronous I/O),Java的I/O体系经历了三次重大演进。

这三种模型分别对应“同步阻塞”“同步非阻塞”“异步非阻塞”三种不同的I/O处理范式,各自适用于不同的业务场景。


一、I/O基础:从操作系统到Java的抽象

1.1 I/O的本质与操作系统角色

I/O操作的本质是程序与外部设备(如磁盘、网络)之间的数据传输。由于外部设备的速度远慢于CPU,直接由CPU等待I/O完成会导致资源浪费。因此,操作系统通过内核缓冲区系统调用来优化I/O流程:程序发起I/O请求后,内核将数据从设备读入内核缓冲区(读操作)或从内核缓冲区写入设备(写操作),程序只需与内核缓冲区交互。

1.2 同步与异步、阻塞与非阻塞

理解BIO、NIO、AIO的关键在于区分两组概念:

  • 同步(Synchronous)vs 异步(Asynchronous):描述“任务完成通知方式”。同步指程序主动查询I/O是否完成;异步指内核在I/O完成后通过事件或回调通知程序。
  • 阻塞(Blocking)vs 非阻塞(Non-blocking):描述“线程在I/O操作期间的状态”。阻塞指线程因等待I/O而挂起;非阻塞指线程在I/O未完成时立即返回,继续执行其他任务。

1.3 Java I/O的演进逻辑

BIO是最原始的模型,简单但低效;NIO通过“多路复用”解决了BIO的线程资源浪费问题;AIO则通过“异步回调”进一步释放了线程在I/O等待期间的计算能力。三者的演进本质是用更高效的方式协调CPU与I/O设备的速度差异


二、BIO:同步阻塞I/O——最原始的“一对一”模型

2.1 BIO的核心特征

BIO(Blocking I/O)是Java最早的I/O模型(JDK 1.0引入),其核心特征是同步阻塞:当程序执行I/O操作(如read()write())时,线程会被阻塞,直到I/O完成。对于网络编程,BIO的典型场景是“一个客户端连接对应一个服务端线程”。

2.2 基础代码示例:传统Socket服务器

以TCP服务端为例,BIO的实现逻辑如下:

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class BioServer {public static void main(String[] args) throws IOException {// 1. 创建服务端Socket,绑定8080端口ServerSocket serverSocket = new ServerSocket(8080);System.out.println("BIO服务器启动,监听端口8080...");// 2. 使用线程池处理客户端连接(避免频繁创建线程)ExecutorService threadPool = Executors.newFixedThreadPool(10);while (true) {// 3. 阻塞等待客户端连接(accept()方法阻塞)Socket clientSocket = serverSocket.accept(); System.out.println("客户端[" + clientSocket.getInetAddress() + "]连接成功");// 4. 为每个客户端分配一个线程处理请求threadPool.execute(() -> {try (InputStream inputStream = clientSocket.getInputStream()) {byte[] buffer = new byte[1024];int len;// 5. 阻塞读取客户端数据(read()方法阻塞)while ((len = inputStream.read(buffer)) != -1) { String message = new String(buffer, 0, len);System.out.println("收到客户端消息:" + message);}} catch (IOException e) {e.printStackTrace();} finally {try {clientSocket.close();} catch (IOException e) {e.printStackTrace();}}});}}
}

2.3 关键操作与阻塞点分析

  • ServerSocket.accept():阻塞等待客户端连接。若没有客户端连接,线程一直挂起。
  • InputStream.read():阻塞读取客户端数据。若客户端未发送数据,线程一直等待。
  • 线程模型:每个客户端连接需要独立线程处理,线程数与连接数1:1。

2.4 优缺点与适用场景

  • 优点:逻辑简单,易于理解和调试;适合处理短连接、低并发场景(如小型工具类服务)。
  • 缺点:线程资源浪费严重(连接数大时线程数爆炸);线程阻塞期间无法执行其他任务,CPU利用率低。
  • 适用场景:连接数少且固定的场景(如数据库直连、内部系统间的短连接通信)。

三、NIO:同步非阻塞I/O——用“多路复用”打破线程限制

3.1 NIO的核心组件

NIO(Non-blocking I/O,JDK 1.4引入)通过通道(Channel)、**缓冲区(Buffer)选择器(Selector)**三大核心组件实现非阻塞I/O。其核心思想是“一个线程管理多个连接”,通过Selector轮询多个Channel的I/O就绪状态,避免为每个连接分配独立线程。

3.1.1 通道(Channel)

Channel是数据传输的双向通道,类似BIO中的InputStream/OutputStream,但支持非阻塞操作。常见实现类:

  • FileChannel(文件I/O)
  • SocketChannel(TCP客户端)
  • ServerSocketChannel(TCP服务端)
  • DatagramChannel(UDP通信)
3.1.2 缓冲区(Buffer)

Buffer是NIO的“数据容器”,所有数据操作必须通过Buffer完成。Buffer是一个固定大小的内存块,支持读/写模式切换(通过flip()方法)。常见实现类:ByteBuffer(最常用)、IntBufferCharBuffer等。

3.1.3 选择器(Selector)

Selector是NIO的“事件引擎”,通过select()方法轮询注册在其上的Channel,检测哪些Channel处于可读、可写或连接就绪状态。一个Selector可以管理成千上万个Channel,实现“单线程处理多连接”。

3.2 基础代码示例:NIO Socket服务器

以TCP服务端为例,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;public class NioServer {public static void main(String[] args) throws IOException {// 1. 创建ServerSocketChannel并绑定端口ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.bind(new InetSocketAddress(8080));serverChannel.configureBlocking(false); // 关键:设置为非阻塞模式// 2. 创建Selector并注册Accept事件Selector selector = Selector.open();serverChannel.register(selector, SelectionKey.OP_ACCEPT); System.out.println("NIO服务器启动,监听端口8080...");while (true) {// 3. 阻塞等待就绪事件(超时时间可设,0表示永久阻塞)int readyChannels = selector.select(); if (readyChannels == 0) continue;// 4. 处理所有就绪事件Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();keyIterator.remove(); // 必须手动移除,避免重复处理// 5. 处理Accept事件(新客户端连接)if (key.isAcceptable()) {ServerSocketChannel ssc = (ServerSocketChannel) key.channel();SocketChannel clientChannel = ssc.accept(); // 非阻塞,可能返回nullif (clientChannel != null) {clientChannel.configureBlocking(false); // 注册Read事件到Selector,使用1024字节的BufferclientChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));System.out.println("客户端[" + clientChannel.getRemoteAddress() + "]连接成功");}}// 6. 处理Read事件(客户端数据可读)else if (key.isReadable()) {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = (ByteBuffer) key.attachment(); // 获取绑定的Buffertry {int len = clientChannel.read(buffer); if (len > 0) {buffer.flip(); // 切换为读模式String message = new String(buffer.array(), 0, buffer.limit());System.out.println("收到客户端消息:" + message);buffer.clear(); // 清空Buffer,准备下次写入} else if (len == -1) {// 客户端关闭连接clientChannel.close();System.out.println("客户端断开连接");}} catch (IOException e) {clientChannel.close();System.out.println("客户端异常断开");}}}}}
}

3.3 关键操作与非阻塞原理

  • configureBlocking(false):将Channel设置为非阻塞模式。此时accept()read()等方法不会阻塞,若I/O未就绪则立即返回(如read()返回0或-1)。
  • Selector的轮询机制:通过selector.select()阻塞等待至少一个Channel就绪(可设置超时时间),避免无意义的空循环。
  • 事件驱动:仅处理就绪的事件(如OP_ACCEPT、OP_READ),线程无需为未就绪的连接浪费资源。

3.4 与BIO的核心差异

维度BIONIO
线程模型1连接1线程(线程池优化)1线程管理N连接(事件驱动)
阻塞点accept()read()全程阻塞selector.select()阻塞
资源消耗高(线程数随连接数线性增长)低(线程数与连接数解耦)
编程复杂度低(逻辑简单)高(需处理Buffer、事件轮询)

3.5 适用场景与注意事项

  • 适用场景:高并发、短连接场景(如HTTP服务器、即时通讯);需注意Selector的轮询效率(避免空轮询导致CPU100%)。
  • Buffer的使用技巧:优先使用DirectByteBuffer(堆外内存)减少内存拷贝;根据业务场景调整Buffer大小(过小导致频繁读写,过大浪费内存)。

四、AIO:异步非阻塞I/O——真正的“回调驱动”模型

4.1 AIO的核心思想

AIO(Asynchronous I/O,JDK 7引入,又称NIO.2)是Java中唯一的异步非阻塞I/O模型。其核心思想是:程序发起I/O操作后立即返回,内核在I/O完成后通过回调函数Future对象通知程序。线程无需等待I/O完成,可继续执行其他任务,真正实现了“I/O与计算并行”。

4.2 核心组件与异步机制

  • AsynchronousChannel:异步通道接口,实现类包括AsynchronousServerSocketChannel(服务端)、AsynchronousSocketChannel(客户端)。
  • CompletionHandler:回调接口,定义completed()(I/O成功)和failed()(I/O失败)方法。
  • Future:表示异步操作的结果,可通过get()方法阻塞等待结果(但会退化为同步)。

4.3 基础代码示例:AIO Socket服务器

以TCP服务端为例,AIO的实现逻辑如下:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;public class AioServer {public static void main(String[] args) throws IOException {// 1. 创建异步服务端Channel并绑定端口AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8080));System.out.println("AIO服务器启动,监听端口8080...");// 2. 注册Accept回调(匿名内部类实现CompletionHandler)serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {@Overridepublic void completed(AsynchronousSocketChannel clientChannel, Void attachment) {// 3. 接受新连接后,递归调用accept()以继续监听其他连接serverChannel.accept(null, this); try {System.out.println("客户端[" + clientChannel.getRemoteAddress() + "]连接成功");ByteBuffer buffer = ByteBuffer.allocate(1024);// 4. 注册Read回调(异步读取客户端数据)clientChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer len, ByteBuffer buffer) {if (len > 0) {buffer.flip();String message = new String(buffer.array(), 0, buffer.limit());System.out.println("收到客户端消息:" + message);buffer.clear();// 继续异步读取(递归调用read())clientChannel.read(buffer, buffer, this);} else if (len == -1) {try {clientChannel.close();System.out.println("客户端断开连接");} catch (IOException e) {e.printStackTrace();}}}@Overridepublic void failed(Throwable exc, ByteBuffer buffer) {try {clientChannel.close();System.out.println("客户端异常断开:" + exc.getMessage());} catch (IOException e) {e.printStackTrace();}}});} catch (IOException e) {e.printStackTrace();}}@Overridepublic void failed(Throwable exc, Void attachment) {System.out.println("Accept失败:" + exc.getMessage());}});// 保持主线程不退出(实际生产环境需更优雅的退出机制)try {Thread.sleep(Long.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}}
}

4.4 异步操作的底层实现

AIO的异步特性依赖于操作系统的异步I/O支持(如Linux的aio、Windows的IOCP)。Java通过本地方法(JNI)调用系统API,内核完成I/O后触发回调。与NIO的“轮询”不同,AIO的“通知”是真正的异步。

4.5 与NIO的核心差异

维度NIO(同步非阻塞)AIO(异步非阻塞)
阻塞模型线程需主动轮询I/O状态内核主动通知I/O完成
线程行为线程在select()时阻塞线程完全不阻塞(仅回调执行)
编程范式事件驱动(轮询+事件处理)回调驱动(内核触发回调)
适用场景高并发短连接(如HTTP)高并发长连接(如文件传输)

4.6 适用场景与注意事项

  • 适用场景:I/O操作耗时较长的场景(如大文件传输、数据库批量操作);需要充分利用CPU资源的高并发场景。
  • 回调地狱问题:多层嵌套的回调可能导致代码可读性下降(可通过CompletableFuture优化);需注意回调函数的线程安全(避免共享变量冲突)。

五、三者对比:从阻塞到异步的演进逻辑

5.1 核心指标对比表

为更直观地理解三者差异,我们从关键维度进行横向对比:

维度BIO(同步阻塞)NIO(同步非阻塞)AIO(异步非阻塞)
I/O范式同步阻塞同步非阻塞异步非阻塞
线程模型1连接1线程(线程池优化)1线程管理N连接(事件驱动)0线程等待I/O(回调驱动)
阻塞点accept()read()全程阻塞selector.select()阻塞无显式阻塞(回调触发时执行)
I/O通知方式程序主动等待(无通知)程序轮询Selector获取就绪事件内核主动回调通知I/O完成
资源消耗高(线程数与连接数线性相关)中(线程数固定,与连接数解耦)低(线程仅处理回调逻辑)
编程复杂度低(线性流程,易调试)中(需处理Buffer、事件轮询)高(回调嵌套,需处理异步状态)
内核交互方式多次系统调用(阻塞等待)单次系统调用(多路复用查询)单次系统调用(异步注册+回调)
典型适用场景低并发短连接(如内部工具)高并发短连接(如HTTP服务器)高并发长I/O(如文件/数据库操作)

5.2 演进逻辑:从“等待I/O”到“利用I/O”

Java I/O模型的三次演进,本质是对“CPU与I/O速度差异”这一核心矛盾的逐步优化:

  • BIO时代:线程直接绑定I/O操作,CPU被迫“等待I/O”。此时I/O效率完全由线程数量决定,但线程是稀缺资源(Java线程默认栈大小1MB,1000个线程需1GB内存),高并发场景下必然崩溃。
  • NIO时代:通过Selector的“多路复用”,将线程从“等待单个连接”解放为“管理多个连接”。CPU不再为未就绪的I/O空转,而是“按需处理”就绪事件,实现了“用更少线程处理更多连接”,但线程仍需主动轮询I/O状态(同步非阻塞的本质)。
  • AIO时代:内核接管I/O的全流程,线程仅需定义“I/O完成后做什么”(回调)。CPU与I/O真正并行——I/O操作在内核空间执行时,线程可继续处理其他任务,彻底释放了“等待时间”,实现“计算与I/O同时进行”。

5.3 如何选择:场景决定模型

实际开发中,I/O模型的选择需结合业务场景的连接数I/O耗时资源约束

  • 选BIO:连接数少(<100)、I/O耗时短(如查询数据库单条记录)、需快速实现的场景。例如小型内部系统的API网关。
  • 选NIO:连接数高(1000+)、I/O耗时短(如HTTP请求处理)、服务器资源有限的场景。例如Spring Boot的默认嵌入式服务器(Tomcat)在高并发时可切换为NIO模式。
  • 选AIO:连接数高(1000+)、I/O耗时长(如大文件上传、数据库批量写入)、需最大化CPU利用率的场景。例如云存储服务的文件传输模块。

5.4 未来趋势:异步化与事件驱动

随着微服务、云原生的普及,高并发、低延迟的需求日益增长。AIO的“异步回调”模式与Reactor(响应式编程)、Netty(高性能网络框架)等技术高度契合,已成为现代分布式系统的底层支撑。未来,结合CompletableFuture的链式回调、Quarkus/Helidon等异步框架的优化,AIO将在更多场景中替代NIO,成为“高效I/O”的代名词。


结语

BIO、NIO、AIO的演进史,是Java对“高效I/O”的持续探索史。从阻塞到非阻塞,从同步到异步,每一次迭代都在更精准地协调CPU与I/O的速度差异。开发者需理解三者的底层逻辑,结合业务场景选择最适合的模型——没有“最好”的I/O模型,只有“最适合”的模型。未来,随着操作系统异步I/O支持的完善(如Linux的io_uring),Java的I/O体系还将继续演进,但“用最少资源完成最多I/O”的核心目标始终不变。

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

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

相关文章

windows PowerToys之无界鼠标:一套键鼠控制多台设备

&#x1f4bb;简介 在多设备协作的工作场景中&#xff0c;如何实现一套键鼠控制多台设备了&#xff1f;微软推出的 PowerToys 工具集中的 Mouse Without Borders&#xff08;无界鼠标&#xff09;&#xff0c;通过软件层实现跨设备的键鼠共享与数据同步功能&#xff0c;为多台…

一道比较难的sql题,筛选出重复字段的行数

select * from 导入数据表; id city_column 1 北京,上海,广州 2 上海,上海,深圳 3 北京,杭州,北京 4 上海,广州,深圳select substring_index(khmc,,,1), * from 导入数据表 truncate table 导入数据表 select count(distinct khmc) from 导入数据表; …

【K8s】整体认识K8s之与集群外部访问--service

这一篇文章主要是对service发现新的理解 为什么要使用service服务发现&#xff1f; 首先pod的IP&#xff0c;是动态的&#xff0c;当我们重启一个pod的时候&#xff0c;它会给它分配一个新的IP&#xff0c;但是如果微服务a想要去调用微服务b&#xff0c;他是需要知道微服务b所有…

k8s(自写)

kubernetes k8s是什么&#xff1f;Kubernetes是什么&#xff1f;架构是怎么样的&#xff1f;6分钟快速入门_哔哩哔哩_bilibili kubernetes是google开源神器&#xff0c;介于应用服务和服务器之间&#xff0c;能够通过策略协调和管理多个应用服务&#xff0c;只需要一个yaml文…

实现微信小程序的UniApp相机组件:拍照、录像与双指缩放

在微信小程序开发中&#xff0c;相机功能已成为许多应用的核心组成部分。本文将介绍如何使用UniApp框架实现一个功能丰富的相机组件&#xff0c;支持拍照、录像、前后摄像头切换以及双指缩放等功能。功能概述这个相机组件具备以下核心功能&#xff1a;拍照功能&#xff1a;支持…

python pyqt5开发DoIP上位机【诊断回复的函数都是怎么调用的?】

目录 文章合集 一、底层网络接收:`_receive_loop`(触发起点) 调用时机: 核心代码: 作用: 二、数据解析:`handle_received_data`(判断是否为诊断回复) 调用时机: 核心代码(诊断回复相关部分): 作用: 三、UI显示:`add_trace_entry`(展示到界面) 调用时机: 信号…

谈物质的运动与运动的物质

运动的物质是不是物质的运动&#xff0c;如果假设是&#xff08;第一假设&#xff09;&#xff0c;那末运动的物质是物质的运动&#xff0c;而运动是物质的根本属性&#xff0c;又运动的物质是物质&#xff0c;则物质的运动是物质&#xff0c;既然运动是物质的根本属性&#xf…

【MLLM】多模态理解Ovis2.5模型架构和训练流程

note 模型架构&#xff1a;延续 Ovis 系列创新的结构化嵌入对齐设计。 Ovis2.5 由三大组件构成&#xff1a;动态分辨率 ViT 高效提取视觉特征&#xff0c;Ovis 视觉词表模块实现视觉与文本嵌入的结构对齐&#xff0c;最后由强大的 Qwen3 作为语言基座&#xff0c;处理多模态嵌…

3.3单链表专题

顺序表这种在标准库已经实现好了&#xff0c;直接调用 pushback pushfront 这些o(1)表示不额外开辟空间src为value继续走&#xff0c;下一个不是value&#xff0c;src值给dst空间&#xff0c;dst&#xff0c;dst刚好等于2&#xff0c;就是新数组长度。若从前向后两个数组元素依…

linux系统学习(15.启动管理)

目录 一、运行级别 1.运行级别 2.运行级别命令 (1)runlevel (2)init 运行级别 3.永久修改启动级别&#xff08;ubantu20.04&#xff09; 二、启动过程 &#x1f539; 总结 三、启动引导程序grub配置文件 一、运行级别 1.运行级别 2.运行级别命令 (1)runlevel (2)ini…

检索优化-混合检索

混合检索&#xff08;Hybrid Search&#xff09;是一种结合了 稀疏向量&#xff08;Sparse Vectors&#xff09; 和 密集向量&#xff08;Dense Vectors&#xff09; 优势的先进搜索技术。旨在同时利用稀疏向量的关键词精确匹配能力和密集向量的语义理解能力&#xff0c;以克服…

Day17(前端:JavaScript基础阶段)

接续上文:Day16(前端:JavaScript基础阶段)_前端题目 csdn-CSDN博客 点关注不迷路哟。你的点赞、收藏&#xff0c;一键三连&#xff0c;是我持续更新的动力哟&#xff01;&#xff01;&#xff01; 主页:一位搞嵌入式的 genius-CSDN博客 系列文章专栏: https://blog.csdn.ne…

OpenCV 轮廓分析实战:从检测到形状匹配的完整指南

轮廓&#xff08;Contour&#xff09;是图像中连续且具有相同灰度值的像素集合&#xff0c;是描述目标形状、位置和结构的核心特征。在计算机视觉中&#xff0c;轮廓分析广泛应用于目标定位、形状识别、尺寸测量等场景&#xff08;如工业零件检测、手写数字识别&#xff09;。本…

2025最新uni-app横屏适配方案:微信小程序全平台兼容实战

以下为uni-app实现微信小程序横屏适配技术方案&#xff0c;包含核心原理、配置方法、代码示例和注意事项&#xff1a;一、横屏适配原理 微信小程序默认采用竖屏模式&#xff0c;横屏适配需通过以下机制实现&#xff1a; 全局配置&#xff1a;在app.json中声明支持横屏页面级配置…

深入解析Nginx常见模块1

在Web服务器和反向代理服务器领域,Nginx凭借其高性能、稳定性和丰富的功能获得了广泛的应用。本文将介绍一些Nginx中常见的模块,帮助你更好地理解和使用它们。 Nginx模块简介 Nginx的模块系统是其强大功能的核心所在,它允许用户根据需要灵活配置服务器的行为。Nginx的模块大…

浅谈new与::operator new

目录 前言 1.为什么C要引入new/delete&#xff1f; 2.operator new与operator delete函数 它们的实际作用 Placement New&#xff08;定位new表达式&#xff09; 总结 前言 在写上一篇博客“vector的模拟实现”时&#xff0c;我一直很好奇vector的private成员为什么要用三个封…

Java中Integer转String

在 Java 中&#xff0c;将 Integer 转换为 String 有多种方法&#xff0c;以下是常见的几种方式&#xff1a;1. 使用 Integer.toString() 方法javaInteger num 123; String str Integer.toString(num); // 直接调用 Integer 的静态方法2. 使用 String.valueOf()javaInteger n…

智能装备如何与软件结合?

一、什么是智能装备&#xff1f; 智能装备是具备“感知-决策-执行-自适应”闭环能力的智能化系统&#xff0c;本质是“传统物理装备”与“数字智能”的深度融合。它不仅能完成预设动作&#xff08;如传统机械臂焊接&#xff09;&#xff0c;还能通过传感器“观察”环境、用算法…

react性能优化有哪些

React 性能优化的手段比较多&#xff0c;既有代码层面的&#xff0c;也有构建层面的&#xff0c;还涉及到运行时调优。我帮你系统性梳理一份&#xff1a;&#x1f539; 一、渲染性能优化1. 减少不必要的渲染React.memo&#xff1a;对函数组件做浅比较&#xff0c;避免相同 prop…

腾讯云OpenCloudOS 9系统部署OpenTenBase数据库详细教程

OpenTenBase简介OpenTenBase是一个关系型数据库集群平台&#xff0c;提供写入可靠性和多节点数据同步功能。可以在一台或多台主机上配置OpenTenBase&#xff0c;并将数据存储在多个物理主机上。OpenTenBase架构组件&#xff1a;Coordinator Node (CN)&#xff1a;应用程序访问入…