当前位置: 首页 > news >正文

java BIO/NIO/AIO

 文章目录

目录

 文章目录

前言

一、BIO(Blocking I/O):同步阻塞模型

核心机制:

1.示例

二、NIO(Non-blocking I/O):同步非阻塞模型

1.Buffer​

底层结构

核心属性

总结

channal:

 Selector(选择器):多路复用控制器

总结

代码示例:

三、AIO

四、redis中的多路复用和NIO中的多路复用

四、总结



前言

BIO->NIO->AIO的转变历程

传统BIO的问题在于高并发时的线程开销,而NIO通过多路复用减少了线程数量,提高了并发能力。但NIO的编程模型复杂,需要处理事件循环、缓冲区管理等。AIO则进一步简化,利用操作系统的异步支持,实现真正的非阻塞,但可能在某些系统上支持不够好,比如Linux的AIO实现不如Windows成熟,所以Netty还是基于NIO。


一、BIO(Blocking I/O):同步阻塞模型

核心机制


每个连接对应一个线程,线程在读写操作时会被阻塞,直到数据就绪。

1.示例

直接看一个代码示例 socket建立连接发送数据(对于每个连接创建一个线程,实现异步通信)

首先需要了解BIO的阻塞点1.连接2.读写

// 服务端代码
public class BioServer {public static void main(String[] args) throws IOException {ServerSocket server = new ServerSocket(8080);System.out.println("BIO 服务端启动,监听端口 8080...");while (true) {// 阻塞点1:等待客户端连接(没有连接时线程挂起)Socket client = server.accept(); System.out.println("客户端连接:" + client.getRemoteSocketAddress());// 为每个连接创建新线程处理读写new Thread(() -> {try {// 阻塞点2:读取客户端数据(无数据时线程挂起)InputStream in = client.getInputStream();BufferedReader reader = new BufferedReader(new InputStreamReader(in));String line;while ((line = reader.readLine()) != null) { // 阻塞直到有数据System.out.println("收到客户端消息: " + line);// 阻塞点3:向客户端写回数据(输出流满时阻塞)OutputStream out = client.getOutputStream();out.write(("服务端响应: " + line + "\n").getBytes());out.flush();}} catch (IOException e) {e.printStackTrace();}}).start();}}
}

这里使用多线程的方式虽然解决了单线程阻塞的情况,但是对每个连都创建一个线程进行读写,如果这时候连接多了,线程太大,占用的资源可想而知 

二、NIO(Non-blocking I/O):同步非阻塞模型

为什么会出现NIO呢,其实就是为了解决BIO单线程阻塞问题(读写阻塞,连接阻塞),实现单线程解决并发问题(多路复用)

核心机制
多路复用器(Selector)轮询事件,单线程处理多个连接,核心是非阻塞和事件驱动。

关键组件

  • Channel(通道):双向通信,替代BIO的流,支持非阻塞模式。

  • Buffer(缓冲区):数据读写的中转区,需配合 flip()clear() 操作。

  • Selector(选择器):监控多个通道的事件(如连接、读、写)。

1.Buffer​

底层结构

Buffer的底层实现是一个​​数组​​,具体类型取决于Buffer的子类,例如

  • ​​ByteBuffer​​底层是byte[]数组;
  • ​​IntBuffer​​底层是int[]数组;
  • 其他类型如CharBuffer、DoubleBuffer等同理。
核心属性

java nio包下看到buffer类里面有这4个核心属性

​​capacity​​:数组的固定长度,创建后不可变。例如,ByteBuffer.allocate(10)会分配一个长度为10的byte[]数组。
​​position​​:指向下一个待读写的位置,初始为0,每次读写后自动递增。
​​limit​​:读写操作的上限。​​写模式​​下,limit等于capacity;​​读模式​​下,limit等于有效数据的末尾位置(由flip()方法设置)。
​​mark​​:通过mark()方法保存position的某个状态,后续可通过reset()恢复到此位置。 

总结

Buffer本质上是​​封装了数组的内存块​​,通过capacity(容量)、position(读写位置)、limit(操作上限)、mark(标记位)四个属性管理数据流。例如,ByteBuffer底层是byte[]数组,其他类型如IntBuffer同理

核心功能​​
​​缓冲作用​​:在I/O操作中,Channel(通道)与Buffer配合,实现数据的批量读写。例如,写数据时先填充Buffer再批量写入Channel,减少直接操作磁盘或网络的次数,提升效率。
​​状态切换​​:通过flip()切换读写模式(写模式下limit=capacity,读模式下limit=position),以及clear()/compact()清空或压缩数据。

channal:

定位:替代传统 BIO 的 Stream,提供双向数据传输能力。

底层接口

主要实现类

类名用途
FileChannel文件 I/O(不支持非阻塞模式)
SocketChannelTCP 网络套接字(客户端/服务端)
ServerSocketChannelTCP 服务端监听套接字
DatagramChannelUDP 数据报通道
Pipe.SinkChannel/SourceChannel线程间通信通道

 Selector(选择器):多路复用控制器

单线程管理多个通道的核心组件,实现 "1个线程处理1000连接"。

是NIO的核心,通过监控多个Channal 实现单个线程并发处理多个请求

selector也位于channals包下:

总结

NIO模型中,每个连接(无论是客户端连接还是服务端监听)都对应一个Channel,将这些Channel注册到同一个Selector上,由Selector统一监控这些Channel的I/O事件(如读、写、连接等)。Selector的核心作用就是用一个线程管理多个Channel,实现高并发。

代码示例:

服务器端:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;public class nioTest {private static final int PORT = 8080;private static final int BUFFER_SIZE = 1024;public static void main(String[] args) throws IOException {// 1. 创建Selector(多路复用器)Selector selector = Selector.open();// 2. 创建ServerSocketChannel并配置非阻塞ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.configureBlocking(false); // 关键:设置为非阻塞模式serverChannel.bind(new InetSocketAddress(PORT));// 3. 注册ACCEPT事件到SelectorserverChannel.register(selector, SelectionKey.OP_ACCEPT);System.out.println("NIO 服务端启动,监听端口: " + PORT);// 4. 事件循环(单线程处理所有连接)while (true) {// 阻塞等待就绪的通道(可设置超时)int readyChannels = selector.select(1000); // 等待1秒if (readyChannels == 0) {System.out.println("等待事件中...");continue;}// 5. 获取就绪的SelectionKey集合Set<SelectionKey> selectedKeys = selector.selectedKeys();Iterator<SelectionKey> keyIterator = selectedKeys.iterator();while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();// 6. 处理连接事件if (key.isAcceptable()) {handleAccept(key, selector);}// 7. 处理读事件if (key.isReadable()) {handleRead(key);}// 8. 处理写事件(通常只在需要时注册)if (key.isWritable()) {handleWrite(key);}// 9. 移除已处理的Key(必须!)keyIterator.remove();}}}// 处理新连接private static void handleAccept(SelectionKey key, Selector selector)throws IOException {ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();// 10. 接受连接(非阻塞,立即返回)SocketChannel clientChannel = serverChannel.accept();if (clientChannel == null) return;System.out.println("客户端连接: " + clientChannel.getRemoteAddress());// 11. 配置客户端通道为非阻塞clientChannel.configureBlocking(false);// 12. 注册读事件,并附加BufferclientChannel.register(selector,SelectionKey.OP_READ,ByteBuffer.allocate(BUFFER_SIZE)  // 附加Buffer对象);}// 处理读数据private static void handleRead(SelectionKey key) throws IOException {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = (ByteBuffer) key.attachment();  // 获取附加的Buffer// 13. 非阻塞读取(不会长时间阻塞!)int bytesRead = clientChannel.read(buffer);if (bytesRead == -1) {  // 客户端关闭连接System.out.println("客户端断开: " + clientChannel.getRemoteAddress());clientChannel.close();return;}if (bytesRead > 0) {// 14. 切换Buffer为读模式buffer.flip();// 15. 处理数据(简单打印)byte[] data = new byte[buffer.remaining()];buffer.get(data);String message = new String(data);System.out.println("收到数据: " + message);// 16. 准备写回响应(注册写事件)key.interestOps(SelectionKey.OP_WRITE);// 17. 保存响应数据(实际应用应更复杂)key.attach(ByteBuffer.wrap(("ECHO: " + message).getBytes()));// 18. 清空Buffer(或compact()处理半包)buffer.clear();}}// 处理写数据private static void handleWrite(SelectionKey key) throws IOException {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buffer = (ByteBuffer) key.attachment();// 19. 非阻塞写入while (buffer.hasRemaining()) {clientChannel.write(buffer);}// 20. 重新注册读事件(取消写事件)key.interestOps(SelectionKey.OP_READ);// 21. 重置Buffer(为下次读准备)buffer.clear();}
}

客户端:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Scanner;public class NioClient {public static void main(String[] args) throws IOException, InterruptedException {// 1. 创建SocketChannel(非阻塞)SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);// 2. 异步连接服务器(立即返回)socketChannel.connect(new InetSocketAddress("127.0.0.1", 8080));// 3. 等待连接完成(非阻塞方式)while (!socketChannel.finishConnect()) {System.out.println("连接建立中...");Thread.sleep(300); // 模拟其他操作}System.out.println("连接服务器成功!");// 4. 用户输入循环Scanner scanner = new Scanner(System.in);while (true) {System.out.print("输入消息: ");String message = scanner.nextLine();// 5. 非阻塞写入ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());while (buffer.hasRemaining()) {socketChannel.write(buffer);}// 6. 非阻塞读取响应ByteBuffer readBuffer = ByteBuffer.allocate(1024);int bytesRead = socketChannel.read(readBuffer);if (bytesRead > 0) {readBuffer.flip();byte[] data = new byte[readBuffer.remaining()];readBuffer.get(data);System.out.println("收到响应: " + new String(data));}}}
}

三、AIO

核心机制
异步回调或Future机制,由操作系统完成IO操作后通知应用,无需应用线程等待。

关键组件

  • AsynchronousServerSocketChannel:异步处理连接。

  • CompletionHandler 或 Future:处理完成事件或获取结果。

工作流程

  1. 服务端通过 accept() 异步接收连接,立即返回。

  2. 操作系统完成连接建立后,回调 CompletionHandler

  3. 读写操作同理,数据就绪后触发回调。

四、redis中的多路复用和NIO中的多路复用

redis也使用了多路复用,但是底层

组件Java NIORedis
语言JavaC 语言
底层依赖JVM 封装的 epoll/kqueue直接调用操作系统的 epoll/kqueue
实现级别JVM 用户空间操作系统内核空间


四、总结

持续更新


http://www.xdnf.cn/news/1003357.html

相关文章:

  • 工具+服务双驱动:创客匠人打造中医IP差异化竞争力
  • 搭建商城系统可能运用到的技术
  • Python告别数据处理卡顿之itertools模块使用详解
  • 立即体验|效果好、低延迟,Trae 已支持 Doubao-1.5-thinking-pro 新模型
  • faiss上的GPU流程,GPU与CPU之间的联系
  • MCP与FunctionCall的区别
  • HALCON第七讲->标定
  • 西电【计算机与网络安全实验】课程期末复习遗留情报
  • git添加全局忽略.DS_Store文件
  • MySQL 和 PostgreSQL,到底选择哪个?
  • 英语作文模板
  • 第八节 工程化与高级特性-模块与命名空间的选择
  • 道可云人工智能每日资讯|雄安人工智能产业园正式开园
  • 循环的嵌套
  • Chroma 向量数据库学习笔记
  • DAY49
  • Vue.js 从入门到实战:用户管理分页表格项目详解
  • 新书速览|CUDA并行编程与性能优化
  • Java大厂面试真题:谢飞机的技术挑战
  • 快速排序:分治思想的经典实践
  • 数据结构 - Java 队列
  • react中hook和高阶组件的选型
  • Windows安装docker及使用
  • nginx学习
  • 【Qt】如何使用QtInstallerFramework打包Qt程序
  • OpenCV CUDA模块图像变形------对图像进行上采样操作函数pyrUp()
  • 134. Gas Station
  • 画图使用说明书
  • 使用adb 抓取perfetto-trace的注意事项
  • [Blender] 高质量材质推荐第四弹:25-30号精选纹理资源详解