在 Java 中,I/O(输入/输出)操作是与外部系统(如文件、网络等)进行数据交换的基础。Java 提供了多种 I/O 机制,包括传统的阻塞式 I/O(BIO)、非阻塞式 I/O(NIO)和异步 I/O(AIO)。本文将详细探讨这三种 I/O 模型的特点、区别及其适用场景。
1 BIO(Blocking I/O)
BIO 是一种同步阻塞 I/O 模型。在这种模型中,线程在执行 I/O 操作时会被阻塞,直到操作完成。BIO 适用于连接数较少且稳定的场景。
特点:
- 同步阻塞:线程在执行 I/O 操作时被阻塞,无法处理其他任务。
- 简单易用:API 直观简单,易于理解和使用。
- 性能较低:每个连接都需要一个单独的线程,适用于连接数较少的场景。
示例代码:
java
public class BioFileDemo {
public static void main(String[] args) {
BioFileDemo demo = new BioFileDemo();
demo.writeFile();
demo.readFile();
}
// 使用 BIO 写入文件
public void writeFile() {
String filename = "logs/itwanger/paicoding.txt";
try {
FileWriter fileWriter = new FileWriter(filename);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write("学编程就上技术派");
bufferedWriter.newLine();
System.out.println("写入完成");
bufferedWriter.close();
fileWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用 BIO 读取文件
public void readFile() {
String filename = "logs/itwanger/paicoding.txt";
try {
FileReader fileReader = new FileReader(filename);
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line;
while ((line = bufferedReader.readLine()) != null) {
System.out.println("读取的内容: " + line);
}
bufferedReader.close();
fileReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2 NIO(New I/O 或 Non-blocking I/O)
NIO 是一种同步非阻塞 I/O 模型。在这种模型中,线程在等待 I/O 时可以执行其他任务,通过 Selector
监控多个 Channel
上的事件,提高性能和可伸缩性。NIO 适用于高并发场景。
特点:
- 同步非阻塞:线程在等待 I/O 时可执行其他任务。
- I/O 多路复用 :通过
Selector
监控多个Channel
上的事件。 - 性能较高:单个线程可以处理多个连接,适用于高并发场景。
示例代码:
java
public class NioFileDemo {
public static void main(String[] args) {
NioFileDemo demo = new NioFileDemo();
demo.writeFile();
demo.readFile();
}
// 使用 NIO 写入文件
public void writeFile() {
Path path = Paths.get("logs/itwanger/paicoding.txt");
try {
FileChannel fileChannel = FileChannel.open(path, EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE));
ByteBuffer buffer = StandardCharsets.UTF_8.encode("学编程就上技术派");
fileChannel.write(buffer);
System.out.println("写入完成");
fileChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用 NIO 读取文件
public void readFile() {
Path path = Paths.get("logs/itwanger/paicoding.txt");
try {
FileChannel fileChannel = FileChannel.open(path, StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = fileChannel.read(buffer);
while (bytesRead != -1) {
buffer.flip();
System.out.println("读取的内容: " + StandardCharsets.UTF_8.decode(buffer));
buffer.clear();
bytesRead = fileChannel.read(buffer);
}
fileChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3 AIO(Asynchronous I/O)
AIO 是一种异步非阻塞 I/O 模型。在这种模型中,线程发起 I/O 请求后立即返回,当 I/O 操作完成时通过回调函数通知线程。AIO 进一步提高了并发处理能力,适用于高吞吐量场景。
特点:
- 异步非阻塞:线程发起 I/O 请求后立即返回,通过回调函数处理结果。
- 高性能:适用于高吞吐量场景,充分利用操作系统参与并发操作。
- 复杂性较高:编程复杂度较高,需要处理回调函数。
示例代码:
java
public class AioDemo {
public static void main(String[] args) {
AioDemo demo = new AioDemo();
demo.writeFile();
demo.readFile();
}
// 使用 AsynchronousFileChannel 写入文件
public void writeFile() {
Path path = Paths.get("logs/itwanger/paicoding.txt");
try {
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
ByteBuffer buffer = StandardCharsets.UTF_8.encode("学编程就上技术派");
Future<Integer> result = fileChannel.write(buffer, 0);
result.get();
System.out.println("写入完成");
fileChannel.close();
} catch (IOException | InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
// 使用 AsynchronousFileChannel 读取文件
public void readFile() {
Path path = Paths.get("logs/itwanger/paicoding.txt");
try {
AsynchronousFileChannel fileChannel = AsynchronousFileChannel.open(path, StandardOpenOption.READ);
ByteBuffer buffer = ByteBuffer.allocate(1024);
fileChannel.read(buffer, 0, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer attachment) {
attachment.flip();
System.out.println("读取的内容: " + StandardCharsets.UTF_8.decode(attachment));
attachment.clear();
try {
fileChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, ByteBuffer attachment) {
System.out.println("读取失败");
exc.printStackTrace();
}
});
Thread.sleep(1000);
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
}
4 三种 I/O 模型的区别
模型 | 同步/异步 | 阻塞/非阻塞 | 适用场景 |
---|---|---|---|
BIO | 同步 | 阻塞 | 连接数较少且稳定的场景 |
NIO | 同步 | 非阻塞 | 高并发场景 (即连接数目多且连接比较短的场景) |
AIO | 异步 | 非阻塞 | 高吞吐量场景 (即连接数目多且连接比较长的场景) |
5 总结
- BIO:采用阻塞式 I/O 模型,线程在执行 I/O 操作时被阻塞,无法处理其他任务,适用于连接数较少且稳定的场景。
- NIO :使用非阻塞 I/O 模型,线程在等待 I/O 时可执行其他任务,通过
Selector
监控多个Channel
上的事件,提高性能和可伸缩性,适用于高并发场景。 - AIO:采用异步 I/O 模型,线程发起 I/O 请求后立即返回,当 I/O 操作完成时通过回调函数通知线程,进一步提高了并发处理能力,适用于高吞吐量场景。
理解这三种 I/O 模型的特点和适用场景,有助于在实际开发中选择合适的 I/O 机制,以提高程序的性能和可扩展性。