BIO(Blocking I/O)、NIO(Non-Blocking I/O)和AIO(Asynchronous I/O)是 Java 中三种不同的 I/O 模型,它们在工作方式、性能、适用场景等方面存在明显区别,以下为你详细介绍:
工作方式
BIO
- 阻塞模式:BIO 是一种传统的 I/O 模型,其核心特点是阻塞。在进行读写操作时,线程会一直阻塞,直到数据准备好或者操作完成。例如,在服务器端接收客户端连接时,如果没有新的连接请求,线程会一直阻塞在
accept()
方法上;在进行数据读写时,线程会阻塞在read()
或write()
方法上,直到数据传输完成。 - 一对一处理:通常一个连接对应一个线程,当有大量并发连接时,需要创建大量的线程来处理,这会导致系统资源的浪费和性能的下降。
示例代码:
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 {
ServerSocket serverSocket = new ServerSocket(8888);
System.out.println("服务器启动,监听端口 8888");
while (true) {
// 阻塞等待客户端连接
Socket socket = serverSocket.accept();
System.out.println("客户端连接成功");
// 为每个连接创建一个新线程处理
new Thread(() -> {
try {
InputStream inputStream = socket.getInputStream();
byte[] buffer = new byte[1024];
int len;
// 阻塞读取数据
while ((len = inputStream.read(buffer)) != -1) {
System.out.println(new String(buffer, 0, len));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
}
}
NIO
- 非阻塞模式:NIO 采用了非阻塞的 I/O 操作方式。线程在进行 I/O 操作时,如果数据还未准备好,不会一直阻塞,而是会立即返回,线程可以继续执行其他任务。通过
Selector
(选择器)可以同时监听多个通道(Channel
)的事件,如连接就绪、读就绪、写就绪等。 - 多路复用:一个线程可以处理多个连接,通过
Selector
不断轮询各个通道的事件,当某个通道有事件发生时,再进行相应的处理,大大提高了系统的并发处理能力。
示例代码:
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;
public class NIOServer {
public static void main(String[] args) throws IOException {
// 创建选择器
Selector selector = Selector.open();
// 创建服务器套接字通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(8888));
serverSocketChannel.configureBlocking(false);
// 注册通道到选择器,监听连接事件
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 阻塞等待事件发生
int readyChannels = selector.select();
if (readyChannels == 0) continue;
Set<SelectionKey> selectedKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = selectedKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = keyIterator.next();
if (key.isAcceptable()) {
// 处理新的连接
ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverChannel.accept();
socketChannel.configureBlocking(false);
// 注册通道到选择器,监听读事件
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
// 处理读事件
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
int bytesRead = socketChannel.read(buffer);
if (bytesRead > 0) {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println(new String(data));
}
}
keyIterator.remove();
}
}
}
}
AIO
- 异步模式:AIO 是真正的异步 I/O 模型。当进行 I/O 操作时,线程只需发起请求,然后可以继续执行其他任务,当 I/O 操作完成后,系统会通过回调函数或 Future 对象通知线程。
- 基于事件和回调机制:AIO 基于事件驱动和回调机制,由操作系统负责完成 I/O 操作,操作完成后会触发相应的回调函数,通知应用程序处理结果。
示例代码:
import java.io.IOException;
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 IOException {
// 创建异步服务器套接字通道
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8888));
// 接受客户端连接
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
// 继续接受下一个连接
serverSocketChannel.accept(null, this);
ByteBuffer buffer = ByteBuffer.allocate(1024);
// 读取数据
socketChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
@Override
public void completed(Integer result, ByteBuffer buffer) {
if (result > 0) {
buffer.flip();
byte[] data = new byte[buffer.remaining()];
buffer.get(data);
System.out.println(new String(data));
}
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
try {
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
@Override
public void failed(Throwable exc, Object attachment) {
exc.printStackTrace();
}
});
// 保持主线程存活
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
性能
- BIO:由于每个连接都需要一个线程来处理,线程的创建和销毁会消耗大量的系统资源,而且线程的阻塞会导致 CPU 资源的浪费,因此在高并发场景下性能较差。
- NIO:采用非阻塞和多路复用的方式,一个线程可以处理多个连接,减少了线程的创建和切换开销,提高了系统的并发处理能力和资源利用率,在中高并发场景下性能较好。
- AIO:基于异步 I/O 和回调机制,由操作系统负责 I/O 操作,应用程序只需处理回调结果,进一步减少了线程的阻塞时间,在高并发、长连接的场景下性能最优。
适用场景
- BIO:适用于连接数较少且固定的场景,如一些传统的企业级应用,对代码的简洁性和可读性要求较高,对并发性能要求不高的场景。
- NIO:适用于连接数较多且连接时间较短(轻操作)的场景,如聊天服务器、Web 服务器等,需要处理大量并发连接的场景。
- AIO:适用于连接数较多且连接时间较长(重操作)的场景,如文件传输、视频流服务等,对 I/O 性能要求较高的场景。