AIO,BIO,NIO的区别(详解)

发布于:2025-04-10 ⋅ 阅读:(34) ⋅ 点赞:(0)

在 Java 编程里,BIO(Blocking I/O)、NIO(Non-blocking I/O)和 AIO(Asynchronous I/O)是三种不同的 I/O 模型,它们在阻塞特性、适用场景等方面存在明显差异。

1.阻塞特性

  • BIO(同步阻塞IO):传统的 I/O 模型,其显著特点是在进行 I/O 操作时会发生阻塞。当一个线程发起一个读或写操作时,该线程会一直等待,直到操作完成,期间无法执行其他任务。例如,在服务器端接收客户端连接时,如果没有新的连接到来,线程会一直阻塞在 accept() 方法上。
  • AIO(异步非阻塞IO):真正的异步 I/O 模型,它基于事件和回调机制。当发起一个 I/O 操作时,线程不会阻塞,并且在操作完成后,系统会通过回调函数通知线程。线程不需要主动去检查操作是否完成,而是由系统在操作完成后主动通知。
  • NIO(同步非阻塞IO):采用了通道(Channel)和缓冲区(Buffer)的概念,通过选择器(Selector)实现了非阻塞的 I/O 操作。线程在发起 I/O 操作后,不会立即阻塞,而是可以继续执行其他任务。当 I/O 操作准备好时,选择器会通知线程进行处理。

2.工作模式

  • AIO:是一种面向流(Stream)的 I/O 模型,数据的读写是基于字节流或字符流进行的。在处理多个客户端连接时,通常需要为每个连接创建一个独立的线程,这会导致线程数量过多,资源消耗大,并且在高并发场景下性能较差。
  • BIO:适用于连接数较多且连接时间较长的场景,如文件传输、大数据处理等。AIO 的异步特性可以充分利用系统资源,提高 I/O 操作的效率。
  • NIO:适用于连接数较多且连接时间较短的场景,如聊天服务器、即时通讯系统等。NIO 可以通过单线程处理多个连接,减少了线程的创建和销毁开销,提高了系统的并发性能。

3.代码实例

BIO:

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) {
        try (AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open()) {
            serverSocketChannel.bind(new InetSocketAddress(8080));
            System.out.println("Server started, listening on port 8080...");

            serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
                @Override
                public void completed(AsynchronousSocketChannel socketChannel, Void attachment) {
                    serverSocketChannel.accept(null, this);
                    System.out.println("New client connected");

                    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[] bytes = new byte[buffer.remaining()];
                                buffer.get(bytes);
                                System.out.println(new String(bytes));
                            }
                        }

                        @Override
                        public void failed(Throwable exc, ByteBuffer buffer) {
                            try {
                                socketChannel.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }

                @Override
                public void failed(Throwable exc, Void attachment) {
                    exc.printStackTrace();
                }
            });

            // 防止主线程退出
            Thread.currentThread().join();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

NIO:

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) {
        try (ServerSocketChannel serverSocketChannel = ServerSocketChannel.open()) {
            serverSocketChannel.bind(new InetSocketAddress(8080));
            serverSocketChannel.configureBlocking(false);

            Selector selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            System.out.println("Server started, listening on port 8080...");

            while (true) {
                selector.select();
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> iterator = selectedKeys.iterator();

                while (iterator.hasNext()) {
                    SelectionKey key = iterator.next();
                    iterator.remove();

                    if (key.isAcceptable()) {
                        ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                        SocketChannel socketChannel = ssc.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                        System.out.println("New client connected");
                    } else if (key.isReadable()) {
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        int length = socketChannel.read(buffer);
                        if (length > 0) {
                            buffer.flip();
                            byte[] bytes = new byte[buffer.remaining()];
                            buffer.get(bytes);
                            System.out.println(new String(bytes));
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 AIO:

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) {
        try (AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open()) {
            serverSocketChannel.bind(new InetSocketAddress(8080));
            System.out.println("Server started, listening on port 8080...");

            serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Void>() {
                @Override
                public void completed(AsynchronousSocketChannel socketChannel, Void attachment) {
                    serverSocketChannel.accept(null, this);
                    System.out.println("New client connected");

                    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[] bytes = new byte[buffer.remaining()];
                                buffer.get(bytes);
                                System.out.println(new String(bytes));
                            }
                        }

                        @Override
                        public void failed(Throwable exc, ByteBuffer buffer) {
                            try {
                                socketChannel.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }

                @Override
                public void failed(Throwable exc, Void attachment) {
                    exc.printStackTrace();
                }
            });

            // 防止主线程退出
            Thread.currentThread().join();
        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        }
    }
}

 从上述代码可以看出,BIO 的代码最为简单,但在高并发场景下性能较差;NIO 通过选择器实现了单线程处理多个连接,代码相对复杂;AIO 基于异步回调机制,代码的复杂度更高,但在处理大量并发连接时性能最优。


网站公告

今日签到

点亮在社区的每一天
去签到