在Java高性能网络编程领域,BIO、NIO、AIO 是最常见的三种I/O模型。理解它们的原理、差异及应用场景,对于选择合适的技术方案、提升系统性能具有重要意义。本文将一次性为你梳理这三种技术的实现原理、常用代码以及最佳实践场景。


一、BIO(Blocking IO)——阻塞式IO

1. 原理简介

BIO即“Blocking IO”,即传统的阻塞式编程模型。每当有一个客户端请求,服务器都需要创建一个独立的线程进行处理,线程与连接一一对应。在读写操作时,线程会被阻塞,直到数据读/写完成。

特点
  • 同步、阻塞:没有数据的时候线程会原地等待。
  • 线程模型简单:一个连接一个线程,容易编程。
  • 资源消耗大,扩展性差:大量连接下需要大量线程。

2. 应用场景

  • 并发量小,业务模型简单,如传统的管理后台、低并发RPC服务。
  • 对延迟或大规模并发请求不敏感的应用。

3. BIO代码示例

服务端
import java.io.*;
import java.net.*;

public class BioServer {
    public static void main(String[] args) throws Exception {
        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("BIO服务器启动,端口8080...");

        while (true) {
            final Socket client = serverSocket.accept(); // 阻塞,直到有客户端连接
            new Thread(() -> {
                try (
                    InputStream in = client.getInputStream();
                    OutputStream out = client.getOutputStream();
                    BufferedReader reader = new BufferedReader(new InputStreamReader(in));
                    PrintWriter writer = new PrintWriter(out, true);
                ) {
                    String line;
                    while ((line = reader.readLine()) != null) { // 阻塞
                        System.out.println("收到客户端:" + line);
                        writer.println("echo:" + line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start(); // 每个连接分配一个线程
        }
    }
}
客户端
import java.io.*;
import java.net.*;

public class BioClient {
    public static void main(String[] args) throws Exception {
        Socket socket = new Socket("localhost", 8080);
        try (
            BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
            BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        ) {
            writer.write("hello, server!\n");
            writer.flush();

            String response = reader.readLine();
            System.out.println("收到服务器:" + response);
        }
    }
}

二、NIO(Non-blocking IO)——非阻塞式IO

1. 原理简介

NIO是Java 1.4引入的“非阻塞式IO”库,又称New IO或Non-blocking IO。与BIO线程一对一不同,NIO采用单线程+多路复用,基于Selector监听多个Channel(通道),只需少量线程即可处理大量连接。

关键组件
  • Channel(通道):类似流,但可读写和异步操作。
  • Buffer(缓冲区):用于存储读/写数据。
  • Selector(选择器):核心,监控多个通道的事件(如可读、可写)。
特点
  • 非阻塞:调用read/write不会一直阻塞。
  • 单线程可处理成千上万连接,而不是“一线程一连接”。
  • 编码复杂,需要手动事件分派和状态管理。

2. 应用场景

  • 高并发服务器端程序,如IM、网关、游戏服务器、分布式高性能RPC等。
  • 需要处理成千上万连接,但连接每秒请求有限的"海量连接、少量数据"场景。

3. NIO代码示例

服务端
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;

public class NioServer {
    public static void main(String[] args) throws Exception {
        Selector selector = Selector.open();
        ServerSocketChannel server = ServerSocketChannel.open();
        server.socket().bind(new InetSocketAddress(8081));
        server.configureBlocking(false);
        server.register(selector, SelectionKey.OP_ACCEPT);

        System.out.println("NIO服务器启动,端口8081...");

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        while (true) {
            selector.select(); // 阻塞,直到有事件到来
            Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
            while (keys.hasNext()) {
                SelectionKey key = keys.next();
                keys.remove();

                if (key.isAcceptable()) {
                    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                    SocketChannel client = ssc.accept();
                    client.configureBlocking(false);
                    client.register(selector, SelectionKey.OP_READ);
                    System.out.println("有新连接进来...");
                }

                if (key.isReadable()) {
                    SocketChannel client = (SocketChannel) key.channel();
                    buffer.clear();
                    int len = client.read(buffer);
                    if (len > 0) {
                        buffer.flip();
                        byte[] data = new byte[len];
                        buffer.get(data, 0, len);
                        System.out.println("收到客户端数据: " + new String(data));
                        // 回写
                        buffer.clear();
                        buffer.put(("echo:" + new String(data)).getBytes());
                        buffer.flip();
                        client.write(buffer);
                    } else if (len == -1) {
                        client.close();
                    }
                }
            }
        }
    }
}
客户端
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;

public class NioClient {
    public static void main(String[] args) throws Exception {
        SocketChannel client = SocketChannel.open(new InetSocketAddress("localhost", 8081));
        client.configureBlocking(false);

        ByteBuffer buffer = ByteBuffer.wrap("hello, nio!\n".getBytes());
        client.write(buffer);

        buffer.clear();
        Thread.sleep(100); // 等待服务器回写
        client.read(buffer);
        buffer.flip();
        System.out.println("收到服务器:" + new String(buffer.array()).trim());
        client.close();
    }
}

三、AIO(Asynchronous IO)——异步非阻塞式IO

1. 原理简介

AIO又称NIO.2,是Java 7引入的“异步IO”模型。AIO底层由操作系统支持(如Linux epoll+线程池,Windows IOCP),读写操作不再等待完成,通过回调函数完成后得知结果。

特点
  • 真正的异步、非阻塞:读写时立即返回,通过操作系统底层机制或线程池处理完后自动回调。
  • 代码难度进一步提高,但极度利于海量并发和异步操作。
  • 对操作系统支持依赖较大。

2. 应用场景

  • 超高并发、对响应延迟极为敏感的服务器应用,如分布式消息服务、推送服务器等。
  • 多为底层高并发框架或Netty等中间件底层使用;实际业务开发直接用AIO较少。

3. AIO代码示例

服务端
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.TimeUnit;

public class AioServer {
    public static void main(String[] args) throws Exception {
        AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(8082));
        System.out.println("AIO服务器启动,端口8082...");

        // 接收连接
        server.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel client, Object attachment) {
                server.accept(null, this); // 递归接受下一个
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                client.read(buffer, 30, TimeUnit.SECONDS, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                    @Override
                    public void completed(Integer result, ByteBuffer attachment) {
                        attachment.flip();
                        String content = new String(attachment.array(), 0, result);
                        System.out.println("收到客户端:" + content);

                        // 回复
                        ByteBuffer outBuf = ByteBuffer.wrap(("echo:" + content).getBytes());
                        client.write(outBuf, outBuf, new CompletionHandler<Integer, ByteBuffer>() {
                            @Override
                            public void completed(Integer result, ByteBuffer attachment) {
                                try { client.close(); } catch (Exception ignored) {}
                            }
                            @Override
                            public void failed(Throwable exc, ByteBuffer attachment) {}
                        });
                    }
                    @Override
                    public void failed(Throwable exc, ByteBuffer attachment) {}
                });
            }
            @Override
            public void failed(Throwable exc, Object attachment) {
                exc.printStackTrace();
            }
        });

        // 主线程阻塞
        Thread.currentThread().join();
    }
}
客户端
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.util.concurrent.Future;

public class AioClient {
    public static void main(String[] args) throws Exception {
        AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
        Future<Void> future = client.connect(new InetSocketAddress("localhost", 8082));
        future.get();

        client.write(ByteBuffer.wrap("hello, aio!\n".getBytes())).get();

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        client.read(buffer).get();
        buffer.flip();
        System.out.println("收到服务器:" + new String(buffer.array(), 0, buffer.remaining()));
        client.close();
    }
}

四、三者对比总结

BIO NIO AIO
是否阻塞 阻塞 非阻塞 非阻塞/异步
线程模型 一连接一线程/多个线程 单线程或少量线程+Selector 完全异步(事件驱动)
编程难度 简单 中等 较高
性能扩展 差:线程资源消耗大 较好(能应对上万连接) 极优
适用场景 小并发后台,老系统 高并发服务器、IM、网关等 超高并发、低延迟
支持平台 通用 通用 需OS底层支持

五、实际应用建议

  • BIO:小型项目、后台管理等低并发场景即可。
  • NIO:当今主流网络服务器的基础,Netty、Tomcat底层都用NIO,适合大部分高并发应用。
  • AIO:在极致并发/异步需求下选用,多用于分布式消息中间件、底层高性能中间件或Netty底层组件,不建议普通业务直接用。
Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐