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 性能要求较高的场景。