后端与系统架构3833 字
从操作系统底层彻底理解Java IO演进之路
·全库 576 / 611
《从操作系统底层彻底理解Java IO演进之路》共 0.4 万字,通篇围绕 并发、场景、安全 展开,归在「后端与系统架构」这一层。
覆盖并发场景安全
适合给 AI 系统搭底座、要管得住的人
目录骨架(8 节)
- **前言**
- **一 BIO**
- **二 NIO(non block)**
- **三 IO多路复用(select、poll、epoll)**
- **四 AIO**
- 五 总结
- 1 发生的系统调用
- 2 优缺点
示例代码
📎 附件:io-demo.zip(zip, 20KB)
原文链接:https://www.yuque.com/attachments/yuque/0/2024/zip/2424104/1713851378092-ef03fca4-ff53-4703-89db-2a92ce542551.zip
分享内容
- BIO优缺点和适用场景
- NIO优缺点和适用场景
- IO多路复用优缺点
- AIO优缺点和适用场景
前言
以操作系统实际调用角度(以 CentOS Linux release 7.5 操作系统为示例),看 IO 的每一步操作到底发生了什么。
关于如何查看系统调用,Linux 可以使用 strace 来查看任何软件的系统调动(这是个很好的分析学习方法):strace -ff -o ./out java Test.Java
一 BIO
BIO(Blocking I/O):同步阻塞型IO
BIO 是 Java 中最早的一种 I/O 模式,它的特点是 I/O 操作会阻塞线程,直到 I/O 操作完成。BIO 的缺点是并发性能较差,因为每个线程都会阻塞等待 I/O 完成。BIO 适用于连接数较少的网络应用,比如 Web 应用中的 Servlet。

分析BIO代码实现
package io;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
public class BIOSocket {
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(8090);
System.out.println("step1: new ServerSocket ");
while (true) {
Socket client = serverSocket.accept();
System.out.println("step2: client\t" + client.getPort());
new Thread(() -> {
try {
InputStream in = client.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
while (true) {
System.out.println(reader.readLine());
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
}
1 发生的系统调用
启动时
执行strace -ff -o ./out java BIOSocket.Java
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 5
bind(5, {sa_family=AF_INET, sin_port=htons(8090), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(5, 50) = 0
poll([{fd=5, events=POLLIN|POLLERR}], 1, -1) = 1 ([{fd=5, revents=POLLIN}])
poll 函数会阻塞直到其中任何一个 FD 发生事件。
FD:Java中FileDescriptor 可以被用来表示开放文件、开放套接字等。具体到NIO中,则用来操作socket套接字,文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。 当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。 在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
有客户端连接后
accept(5, {sa_family=AF_INET, sin_port=htons(10253), sin_addr=inet_addr("127.0.0.1")}, [16]) = 6
clone(child_stack=0x7f013e5c4fb0, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, parent_tidptr=0x7f013e5c59d0, tls=0x7f013e5c5700, child_tidptr=0x7f013e5c59d0) = 13168
poll([{fd=5, events=POLLIN|POLLERR}], 1, -1