1、BIO(Blocking I/O,同步阻塞I/O)
-
定义:传统的同步阻塞模型,线程在读写数据时会一直阻塞,直到操作完成。
-
工作原理:
- 每个客户端连接需要一个独立的线程处理。
- 线程在
read()或write()时会被阻塞,无法执行其他任务。
-
特点:
- 简单易用:代码逻辑直观。
- 高资源消耗:每个连接占用一个线程,并发量高时线程数激增,易导致性能瓶颈。
-
适用场景:
- 低并发、连接数较少的应用(如简单文件读写)。
-
示例:
ServerSocket serverSocket = new ServerSocket(8080); while (true) { Socket socket = serverSocket.accept(); // 阻塞等待连接 new Thread(() -> { // 处理读写操作 }).start(); }对于以上代码,我想到了下面的可视化解释:
想象一个公司前台,服务器就像是一个门,门的编号是 8080。
这个门会一直等待有人敲门(客户端连接)。ServerSocket serverSocket = new ServerSocket(8080);
这是创建一个门(服务器),门的编号是 8080。门会一直开着,等待有人敲门。while (true)
这是一个无限循环,表示门会一直开着,不会关闭。Socket socket = serverSocket.accept();
这是门在等待有人敲门。当有人敲门时,门会接受这个请求,并生成一个“接待员”(Socket 对象)来处理这个客户。new Thread(() -> { ... }).start();
每当有客户敲门,服务器会分配一个接待员(线程)来专门处理这个客户的需求。这样,即使门还在等待其他客户,接待员可以独立地处理当前客户的请求。
2. NIO(Non-blocking I/O,同步非阻塞I/O)
-
定义:基于事件驱动的多路复用模型,线程不会被单个I/O操作阻塞。
-
核心组件:
- Channel:替代传统流(Stream),支持双向读写(如
SocketChannel、ServerSocketChannel)。 - Buffer:数据读写的中转缓冲区(如
ByteBuffer)。 - Selector:多路复用器,监听多个通道的事件(如连接、读、写)。
- Channel:替代传统流(Stream),支持双向读写(如
-
工作原理:
- 线程通过
Selector轮询注册的Channel,仅处理已就绪的事件。 - 非阻塞模式下,
read()和write()立即返回,若数据未就绪,线程可执行其他任务。
- 线程通过
-
特点:
- 高并发支持:单线程可管理多个连接,减少线程资源消耗。
- 复杂实现:需要处理事件循环、缓冲区管理等。
-
适用场景:
- 高并发、短连接场景(如聊天服务器、即时通讯)。
-
示例:
Selector selector = Selector.open(); ServerSocketChannel serverChannel = ServerSocketChannel.open(); serverChannel.bind(new InetSocketAddress(8080)); serverChannel.configureBlocking(false); serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册连接事件 while (true) { selector.select(); // 阻塞直到有事件就绪 Set<SelectionKey> keys = selector.selectedKeys(); for (SelectionKey key : keys) { if (key.isAcceptable()) { // 处理新连接 } else if (key.isReadable()) { // 处理读事件 } } }同样的,对于这段代码给出以下的解释:
想象一个大楼的门禁系统:
-
Selector是一个警报器,它可以同时监控多个门(Channel)。 -
ServerSocketChannel是大楼的主门,门的编号是 8080。 -
SelectionKey是警报器上的一个指示灯,表示某个门的状态(比如有人敲门或门铃响了)。 -
selector.select()是警报器在等待,直到有门的状态发生变化(比如有人敲门或门铃响了)。 -
SelectionKey是警报器上的一个指示灯,表示某个门的状态:isAcceptable():表示有人敲门(新连接请求)。isReadable():表示门铃响了(数据可读)。
Selector selector = Selector.open(); // 创建一个警报器 ServerSocketChannel serverChannel = ServerSocketChannel.open(); // 创建主门 serverChannel.bind(new InetSocketAddress(8080)); // 主门的编号是 8080 serverChannel.configureBlocking(false); // 主门设置为非阻塞模式 serverChannel.register(selector, SelectionKey.OP_ACCEPT); // 注册警报器,监听“有人敲门”事件 while (true) { // 无限循环,保持警报器工作 selector.select(); // 警报器阻塞等待事件发生 Set<SelectionKey> keys = selector.selectedKeys(); // 获取所有触发的事件 for (SelectionKey key : keys) { if (key.isAcceptable()) { // 如果是“有人敲门”事件 // 处理新连接(比如接受连接并注册到警报器) } else if (key.isReadable()) { // 如果是“门铃响了”事件 // 处理读操作(比如读取客户端发送的数据) } } }
3. AIO(Asynchronous I/O,异步非阻塞I/O)
-
定义:基于事件回调的异步模型,I/O操作完成后系统主动通知线程。
-
工作原理:
- 发起
read()或write()后,线程继续执行其他任务。 - 操作系统完成I/O操作后,通过回调(如
CompletionHandler)通知结果。
- 发起
-
特点:
- 真正的异步:无需轮询,资源利用率高。
- 编程复杂度高:回调逻辑需妥善处理,避免嵌套地狱。
-
适用场景:
- 高吞吐量、长连接场景(如文件服务器、大规模数据传输)。
-
示例:
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(); server.bind(new InetSocketAddress(8080)); server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() { @Override public void completed(AsynchronousSocketChannel client, Void attachment) { ByteBuffer buffer = ByteBuffer.allocate(1024); client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() { @Override public void completed(Integer result, ByteBuffer buffer) { // 处理读取的数据 } @Override public void failed(Throwable exc, ByteBuffer buffer) {} }); } @Override public void failed(Throwable exc, Void attachment) {} });同样的,我想到了一个比较完美的解释
想象一个快递站(服务器),它等待快递员(客户端)来送包裹(数据)。快递站不需要一直盯着门口,而是当有快递员来时,会自动处理包裹。
AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open(); // 创建快递站
server.bind(new InetSocketAddress(8080)); // 快递站的地址是 8080
server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() { // 快递站开始等待快递员
@Override
public void completed(AsynchronousSocketChannel client, Void attachment) { // 当快递员到达时
ByteBuffer buffer = ByteBuffer.allocate(1024); // 准备一个包裹(缓冲区)
client.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() { // 快递员开始处理包裹
@Override
public void completed(Integer result, ByteBuffer buffer) { // 包裹处理完成
// 处理读取的数据(比如解析数据或发送响应)
}
@Override
public void failed(Throwable exc, ByteBuffer buffer) { // 包裹处理失败
// 处理错误
}
});
}
@Override
public void failed(Throwable exc, Void attachment) { // 快递员未到达(连接失败)
// 处理连接失败
}
});