Skip to content

BIO、NIO


BIO(Blocking I/O)

一、是什么?
BIO(阻塞I/O)是Java传统的同步阻塞I/O模型。当线程执行读写操作时,必须等待数据完全就绪(如客户端发送数据或连接建立),期间线程会被阻塞,无法执行其他任务。

二、解决什么问题
BIO适用于简单的客户端/服务器场景,如低并发应用。它通过多线程(每个连接一个线程)处理多个客户端请求,解决基础网络通信问题。

三、核心方法

  • ServerSocket.accept():阻塞等待客户端连接。
  • InputStream.read():阻塞读取客户端数据。
  • 典型流程:主线程循环监听连接,为每个连接创建新线程处理I/O。

四、应用场景

  • 低并发应用(如内部管理系统)。
  • 客户端数量少且连接时间短的场景。
  • 简单原型开发(代码简单易实现)。

五、Java示例

java
// 服务端代码
public class BioServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        while (true) {
            Socket clientSocket = serverSocket.accept(); // 阻塞等待连接
            new Thread(() -> {
                try
                    BufferedReader reader = new BufferedReader(
                        new InputStreamReader(clientSocket.getInputStream()));
                    String request = reader.readLine(); // 阻塞读取数据
                    System.out.println("收到请求: " + request);
                    // 处理请求并响应
                } catch (IOException e) { e.printStackTrace(); }
           ).start();
        }
    }
}

六、重要注意事项

  • 线程开销大:每个连接需独立线程,消耗内存(默认1MB/线程),高并发时易崩溃。
  • 资源浪费:线程阻塞时CPU闲置。
  • 适用性:避免用于高并发场景(如即时通讯、游戏服务器)。

NIO(Non-blocking I/O)

一、是什么?
NIO(非阻塞I/O)是Java 1.4引入的异步I/O模型。核心是通道(Channel)缓冲区(Buffer)选择器(Selector),线程通过选择器轮询多个通道的就绪状态,无需阻塞等待。

二、解决什么问题
解决BIO的高并发瓶颈:

  1. 单线程管理多连接:通过Selector轮询,一个线程处理数千连接。
  2. 零拷贝技术:减少数据在内存中的复制次数,提升传输效率。
  3. 避免线程阻塞:线程只在有就绪事件时工作,否则执行其他任务。

三、核心组件

  1. Channel:双向通信管道(如ServerSocketChannel)。
  2. Buffer:数据容器(如ByteBuffer)。
  3. Selector:监听多个Channel的事件(如连接、读写就绪)。

四、应用场景

  • 高并发服务(如聊天服务器、API网关)。
  • 需要长连接的应用(如WebSocket)。
  • 大数据传输(零拷贝优化性能)。

五、Java示例

java
// 服务端代码
public class NioServer {
    public static void main(String[] args) throws IOException {
        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();
            Iterator<SelectionKey> iter = keys.iterator();
            while (iter.hasNext()) {
                SelectionKey key = iter.next();
                if (key.isAcceptable()) {
                    // 处理新连接
                    SocketChannel client = serverChannel.accept();
                    client.configureBlocking(false);
                    client.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    // 读取数据(非阻塞)
                    SocketChannel client = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    client.read(buffer);
                    buffer.flip();
                    System.out.println("收到数据: " + new String(buffer.array()));
                }
                iter.remove();
            }
        }
    }
}

六、重要注意事项

  • 编程复杂:需处理事件循环、缓冲区状态(flip/clear)。
  • 空轮询问题:Linux下Selector可能因Bug无限循环,需用epoll替代。
  • 适用性:适合高并发,但简单场景用BIO更快捷。

BIO vs NIO 区别

特性BIONIO
阻塞方式同步阻塞(线程必须等待)同步非阻塞(线程轮询事件)
线程模型1连接 = 1线程1线程处理多连接
并发能力低(线程数限制)高(可支持数万连接)
资源消耗高(线程内存开销大)低(少量线程+事件驱动)
适用场景低并发、短连接高并发、长连接
复杂度简单(直同步代码)复杂(需理解Selector/Buffer)

总结

  • BIO:简单易用,适合低并发场景,但资源效率低。
  • NIO:复杂但高效,适合高并发和长连接,需掌握事件驱动模型。
  • 选择建议
    • 快速开发小应用 → BIO。
    • 高性能服务器(如Netty底层)→ NIO。
    • Java 7+推荐使用更简化的NIO.2(AIO)。