BIO(Blocking I/O)、NIO(Non-blocking I/O)和AIO(Asynchronous I/O)是三种不同的I/O模型,它们在处理输入输出操作时有着不同的特点和适用场景,下面为你详细介绍:
BIO(Blocking I/O)
概念
BIO即阻塞式I/O,是一种传统的I/O模型。在该模型里,当进行I/O操作(像读取或写入数据)时,线程会被阻塞,直至操作完成。也就是说,在数据传输过程中,线程无法执行其他任务,只能等待。
工作流程
以服务器端接收客户端连接为例,典型的BIO工作流程如下:
- 服务器端创建一个
ServerSocket
,并调用accept()
方法等待客户端连接。此时线程会被阻塞,直到有客户端连接进来。 - 客户端连接成功后,服务器端为该客户端创建一个新的
Socket
对象,并为其分配一个新的线程来处理该客户端的请求。 - 在处理客户端请求时,线程会调用
InputStream
或OutputStream
进行数据的读取或写入操作,这些操作同样会使线程阻塞,直至数据传输完成。
示例代码(Java)
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(Non-blocking I/O)
概念
NIO即非阻塞式I/O,是Java 1.4引入的新I/O模型。在NIO模型中,线程在进行I/O操作时不会被阻塞,而是可以继续执行其他任务。NIO通过通道(Channel)和缓冲区(Buffer)进行数据传输,使用选择器(Selector)来实现多路复用。
工作流程
- 服务器端创建一个
ServerSocketChannel
,并将其配置为非阻塞模式。 - 创建一个
Selector
,并将ServerSocketChannel
注册到Selector
上,监听连接事件。 - 线程调用
Selector
的select()
方法,该方法会阻塞,直到有事件发生。 - 当有事件发生时,
Selector
会返回发生事件的通道集合,线程可以遍历这些通道,根据事件类型进行相应的处理。
示例代码(Java)
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 {
// 创建ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(8888));
serverSocketChannel.configureBlocking(false);
// 创建Selector
Selector selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("服务器启动,监听端口 8888");
while (true) {
// 阻塞等待事件发生
if (selector.select() == 0) {
continue;
}
// 获取发生事件的SelectionKey集合
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isAcceptable()) {
// 处理连接事件
ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = ssc.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 len;
while ((len = socketChannel.read(buffer)) > 0) {
buffer.flip();
System.out.println(new String(buffer.array(), 0, len));
buffer.clear();
}
}
// 移除已处理的SelectionKey
iterator.remove();
}
}
}
}
优缺点
- 优点:通过多路复用技术,一个线程可以处理多个连接,减少了线程的创建和上下文切换开销,提高了系统的并发处理能力。
- 缺点:实现相对复杂,需要对通道、缓冲区和选择器等概念有深入的理解。
AIO(Asynchronous I/O)
概念
AIO即异步I/O,是Java 7引入的异步I/O模型。在AIO模型中,当进行I/O操作时,线程不会被阻塞,并且不需要主动去检查操作是否完成。当I/O操作完成后,系统会通过回调函数或Future对象通知线程。
工作流程
- 服务器端创建一个
AsynchronousServerSocketChannel
,并绑定到指定端口。 - 调用
AsynchronousServerSocketChannel
的accept()
方法,该方法会立即返回,不会阻塞线程。当有客户端连接进来时,系统会通过回调函数通知线程。 - 对于数据的读取和写入操作,同样是异步进行的,操作完成后会通过回调函数通知线程。
示例代码(Java)
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;
import java.util.concurrent.ExecutionException;
public class AIOServer {
public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
// 创建AsynchronousServerSocketChannel
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
serverSocketChannel.bind(new InetSocketAddress(8888));
System.out.println("服务器启动,监听端口 8888");
// 接受客户端连接
serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
@Override
public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
// 继续接受下一个客户端连接
serverSocketChannel.accept(null, this);
try {
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();
System.out.println(new String(buffer.array(), 0, result));
}
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) {
exc.printStackTrace();
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void failed(Throwable exc, Object attachment) {
exc.printStackTrace();
}
});
// 防止主线程退出
Thread.sleep(Long.MAX_VALUE);
}
}
优缺点
- 优点:真正实现了异步操作,线程在进行I/O操作时不需要等待,提高了系统的性能和响应速度。
- 缺点:实现复杂度较高,并且对操作系统和硬件有一定的要求。
综上所述,BIO适用于连接数较少且固定的场景;NIO适用于连接数较多但并发量不高的场景;AIO适用于连接数较多且并发量高的场景。