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

JavaIO笔记

一、BIO(阻塞 IO)版本

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;public class BIOServer {public static void main(String[] args) throws IOException {// 1️⃣ 用户态:创建服务端Socket(底层通过syscall创建socket fd)ServerSocket serverSocket = new ServerSocket(8080);System.out.println("BIO服务器启动,监听8080...");while (true) {// 2️⃣ 阻塞等待客户端连接(accept是系统调用,会进入内核态)Socket socket = serverSocket.accept(); // <-- 阻塞点System.out.println("客户端已连接:" + socket.getRemoteSocketAddress());// 3️⃣ 读取客户端发送的数据(若内核缓冲区为空则阻塞)InputStream inputStream = socket.getInputStream();byte[] buffer = new byte[1024];int len;while ((len = inputStream.read(buffer)) != -1) { // <-- 阻塞点String msg = new String(buffer, 0, len);System.out.println("收到客户端消息:" + msg);}// 4️⃣ 客户端断开socket.close();}}
}

内核交互图

用户线程(Java层)        操作系统(内核态)
----------------------------------------------------
accept()  ------------>  内核等待新连接队列非空(若无连接则阻塞线程)read()    ------------>  内核TCP缓冲区中若无数据 → 阻塞线程若有数据 → 拷贝至用户空间 → 返回

结论:
BIO 每次 I/O 调用都会让线程阻塞,CPU 不能干别的事,因此每个连接都要一个线程去读。


二、NIO(非阻塞 IO + 多路复用)版本

我们使用 ServerSocketChannel + Selector 的典型写法👇

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;public class NIOServer {public static void main(String[] args) throws IOException {// 1️⃣ 创建 ServerSocketChannel(本质上仍是一个socket fd)ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.bind(new InetSocketAddress(8080));serverChannel.configureBlocking(false); // 设置为非阻塞模式System.out.println("NIO服务器启动,监听8080...");// 2️⃣ 创建 Selector(内核底层 epoll/kqueue)Selector selector = Selector.open();// 3️⃣ 将 ServerSocketChannel 注册到 selector,监听“连接就绪”事件serverChannel.register(selector, SelectionKey.OP_ACCEPT);// 4️⃣ 事件循环while (true) {System.out.println("等待事件发生...");// select() 会阻塞,直到至少有一个通道就绪selector.select(); // <-- 阻塞在多路复用器上(操作系统监视所有fd)// 5️⃣ 获取就绪事件集合Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();while (iterator.hasNext()) {SelectionKey key = iterator.next();iterator.remove(); // 取出后要移除,避免重复处理// 6️⃣ 处理不同类型事件if (key.isAcceptable()) { // 新连接事件ServerSocketChannel ssc = (ServerSocketChannel) key.channel();SocketChannel client = ssc.accept(); // 取出新连接client.configureBlocking(false);System.out.println("新客户端连接:" + client.getRemoteAddress());client.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));} else if (key.isReadable()) { // 可读事件SocketChannel client = (SocketChannel) key.channel();ByteBuffer buffer = (ByteBuffer) key.attachment();int len = client.read(buffer); // 非阻塞readif (len > 0) {buffer.flip();System.out.println("收到消息:" + new String(buffer.array(), 0, len));buffer.clear();} else if (len == -1) {System.out.println("客户端断开:" + client.getRemoteAddress());client.close();}}}}}
}

内核层流程图解

初始化阶段

ServerSocketChannel.open()   → syscall(socket)
Selector.open()              → syscall(epoll_create)
register()                   → syscall(epoll_ctl(ADD, fd, event))

事件循环阶段

selector.select()            → syscall(epoll_wait)内核阻塞,等待任意fd状态变化(连接到达、可读、可写)== 事件触发 ==
内核检测到:- 某fd可读(TCP缓冲区有数据)- 某fd可写- 新连接到达监听fd== 内核通知用户态 ==
selector.select() 返回
→ Java线程被唤醒
→ 用户态开始处理事件

内核交互图(NIO版)

用户线程(Java层)        操作系统(内核态)
----------------------------------------------------
register() ------------> epoll_ctl(ADD)内核记录:此fd要监听哪些事件select()  ------------> epoll_wait()等待任何一个fd状态变化若事件发生 → 唤醒线程read()    ------------> 非阻塞调用,若无数据立即返回0若有数据 → 内核拷贝 → 用户空间返回

精华对比总结

对比点

BIO

NIO

编程接口

InputStream / OutputStream

Channel / Buffer

阻塞模型

调用 read() 时阻塞

非阻塞,立即返回

并发模型

一连接一线程

一个线程监听所有连接

多路复用

❌ 无

✅ 由 Selector + epoll 实现

select() 是否阻塞

✅ 是,但只阻塞在等待事件阶段(而非数据读取阶段)

数据读取

内核 → 用户缓冲区

通道对应独立缓冲区(ByteBuffer)

缓冲区积压

每个通道独立维护,受内核 TCP buffer 限制

控制得当不会溢出(epoll 水平触发或边缘触发控制)

操作系统交互

每次I/O调用都会syscall

注册后由epoll_wait监控fd状态

AIO

一、AIO(Asynchronous I/O)核心思想

AIO 是 Java 7 (java.nio.channels 包) 引入的真正的异步非阻塞 I/O 模型

不同于 NIO 的“一个线程轮询多个通道”,AIO 是由操作系统完成 I/O 并主动通知应用线程
即:

  • 发起请求 → 操作系统后台执行 → I/O 完成后回调用户定义的处理逻辑(CompletionHandler)。

底层机制(重点说明给面试官听)

在 Linux 下,Java 的 AIO 实际上是基于底层的 epoll + 线程池 模拟异步,而在 Windows 上是基于系统的真正异步 I/O API(如 IOCP)。
所以你可以总结:

“AIO 是真正的异步模型,数据读写都由内核完成后再通知应用层回调。相比之下,NIO 只是多路复用,线程仍需主动去取数据。”


二、AIO 经典实现代码示例

这是最常见的 AIO 服务端和客户端实现(基于 AsynchronousServerSocketChannelAsynchronousSocketChannel)👇

服务端示例:异步接受客户端请求并读取数据

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 Exception {// 1. 打开异步 Server 通道AsynchronousServerSocketChannel server =AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8888));System.out.println("AIO Server started on port 8888...");// 2. 异步等待客户端连接server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {@Overridepublic void completed(AsynchronousSocketChannel client, Void attachment) {// 继续接受下一个连接(关键)server.accept(null, this);ByteBuffer buffer = ByteBuffer.allocate(1024);// 3. 异步读取客户端数据client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result, ByteBuffer buf) {buf.flip();String msg = new String(buf.array(), 0, result);System.out.println("Received: " + msg);// 4. 异步写回响应ByteBuffer response = ByteBuffer.wrap(("Echo: " + msg).getBytes());client.write(response);}@Overridepublic void failed(Throwable exc, ByteBuffer buf) {exc.printStackTrace();}});}@Overridepublic void failed(Throwable exc, Void attachment) {exc.printStackTrace();}});// 主线程阻塞(防止退出)Thread.currentThread().join();}
}

客户端示例

import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;public class AioClient {public static void main(String[] args) throws Exception {AsynchronousSocketChannel client = AsynchronousSocketChannel.open();client.connect(new InetSocketAddress("127.0.0.1", 8888), null,new CompletionHandler<Void, Void>() {@Overridepublic void completed(Void result, Void attachment) {ByteBuffer buffer = ByteBuffer.wrap("Hello AIO".getBytes());client.write(buffer);ByteBuffer readBuffer = ByteBuffer.allocate(1024);client.read(readBuffer, readBuffer, new CompletionHandler<Integer, ByteBuffer>() {@Overridepublic void completed(Integer result, ByteBuffer buf) {buf.flip();System.out.println("Server replied: " +new String(buf.array(), 0, result));}@Overridepublic void failed(Throwable exc, ByteBuffer buf) {exc.printStackTrace();}});}@Overridepublic void failed(Throwable exc, Void attachment) {exc.printStackTrace();}});Thread.sleep(3000);}
}

三、AIO vs NIO 对比(重点总结)

对比项

BIO

NIO

AIO

模型类型

同步阻塞

同步非阻塞

异步非阻塞

I/O 调用

阻塞调用

Selector 轮询

回调通知

底层机制

每连接一线程

I/O 多路复用(epoll/select)

内核完成后回调(IOCP/模拟异步)

线程模型

一个连接一个线程

一个线程可管理多个连接

线程发起请求后立刻返回,由回调处理

适用场景

连接少、简单业务

高并发、短连接场景(如聊天)

超高并发、I/O 密集、任务耗时较长场景

Java 包

java.io

java.nio

java.nio.channels(NIO2)

http://www.dtcms.com/a/540806.html

相关文章:

  • Agentic AI 与 AI Agent的核心区别
  • 广西网站开发建设定州网站建设公司
  • 医疗营销网站建设方案帝国cms建站实例教程
  • Docker Compose:从单容器到多容器一键部署
  • 开源图像与视频过曝检测工具:HSV色彩空间分析与时序平滑处理技术详解​
  • 分布式版本控制系统Gitlab
  • 商城推荐系统|基于SprinBoot+vue的商城推荐系统(源码+数据库+文档)
  • 可以写代码的网站平面设计考研科目
  • Python数据分析实战:基于上证指数历史数据的时间序列与特征分析应用【数据集可下载】
  • 延迟双删介绍
  • 如何自学开发
  • 游戏类企业网站模板wordpress为什么进不去了
  • 告别设备限制!CodeServer+cpolar让VS Code随时随地在线编程
  • n8n安装教程和快速开始实现模型对话
  • DooTask 1.3.38 版本更新:MCP 服务器与 AI 工具深度融合,开启任务管理新体验
  • 14天极限复习软考day5-23年真题
  • 【面试高频】手写 Promise 四大并发方法
  • 所有网站的名字大全爱客装修官网
  • 互联网大厂Java面试实录:Spring Boot微服务架构实战与JVM深度调优
  • Comparator 比较器 算法排序工具
  • 做网站的流量怎么算钱浦东网站建设箱海运
  • 【LeetCode热题100(55/100)】子集
  • 网站底部设计代码响应式网站检测工具
  • 前端开发,为什么容易被边缘化?
  • PartList::getNamedPart(...)获取对象为空
  • 做食品网站有哪些东西企业信息管理系统erp
  • 廊坊网站制作费用青海西宁网页网站制作
  • 东莞好的网站建设哪家好做设计找素材那个网站最好用
  • php 怎么做视频网站陕西省建设网三类人员成绩公示
  • 多模态的大模型文本分类模型代码(一)——准备阶段