【Java NIO】
这里写目录标题
- 为什么需要 NIO?传统 I/O 的局限性
- 二级目录
- 三级目录
Java I/O support is included in the java.io and java.nio packages. Together these packages include the following features:
- Input and output through data streams, serialization and the file system.
- Charsets, decoders, and encoders, for translating between bytes and Unicode characters.
- Access to file, file attributes and file systems.
- APIs for building scalable servers using asynchronous or multiplexed, non-blocking I/O.
为什么需要 NIO?传统 I/O 的局限性
- 阻塞式 I/O(BIO)的问题
传统 InputStream/OutputStream 是同步阻塞模型:线程在读写数据时必须等待操作完成,无法处理其他任务。
import java.io.FileInputStream;
import java.io.InputStream;
public class BlockingExample {
public static void main(String[] args) {
try (InputStream is = new FileInputStream("large_file.txt")) {
byte[] buffer = new byte[1024];
int bytesRead;
// 同步阻塞点:read() 会阻塞直到数据读取完成
while ((bytesRead = is.read(buffer)) != -1) {
System.out.println("读取到 " + bytesRead + " 字节数据");
// 模拟处理数据(假设处理耗时)
Thread.sleep(1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
代码解释
is.read(buffer) 是同步阻塞的:
-
当调用 read() 方法时,线程会一直等待,直到从文件中读取到数据(或到达文件末尾)。
-
如果文件很大或磁盘速度慢,线程会长时间阻塞在此处,无法执行其他任务。
整个过程是同步的:
- 必须等待 read() 完成,才能进入下一次循环或处理数据(如 Thread.sleep(1000))。
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class BlockingSocketServer {
public static void main(String[] args) {
try (ServerSocket serverSocket = new ServerSocket(8080)) {
while (true) {
// 同步阻塞点 1:accept() 阻塞,直到有客户端连接
Socket clientSocket = serverSocket.accept();
System.out.println("客户端连接:" + clientSocket.getRemoteSocketAddress());
// 为每个客户端连接分配一个线程处理(传统 BIO 的典型做法)
new Thread(() -> {
try (InputStream is = clientSocket.getInputStream()) {
byte[] buffer = new byte[1024];
int bytesRead;
// 同步阻塞点 2:read() 阻塞,直到客户端发送数据
while ((bytesRead = is.read(buffer)) != -1) {
String data = new String(buffer, 0, bytesRead);
System.out.println("收到数据:" + data);
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
高并发场景下(如服务器处理大量连接),因为需要为每个连接分配独立线程,所以导致有大量的线程,导致线程资源耗尽和大量的CPU资源用于上下文切换开销。
- 内存映射与零拷贝的缺失
传统 I/O 需要多次数据拷贝(用户态 ↔ 内核态),而 NIO 的 FileChannel 支持内存映射文件(MappedByteBuffer),减少拷贝次数,提升性能。
- 非阻塞与事件驱动需求
传统 I/O 无法实现非阻塞操作,难以应对高并发、实时性要求高的场景(如聊天服务器、实时交易系统)。