BIO/NIO/AIO

BIO(Blocking I/O)、NIO(Non-blocking I/O)和AIO(Asynchronous I/O)是三种不同的I/O模型,它们在处理输入输出操作时有着不同的特点和适用场景,下面为你详细介绍:

BIO(Blocking I/O)

概念

BIO即阻塞式I/O,是一种传统的I/O模型。在该模型里,当进行I/O操作(像读取或写入数据)时,线程会被阻塞,直至操作完成。也就是说,在数据传输过程中,线程无法执行其他任务,只能等待。

工作流程

以服务器端接收客户端连接为例,典型的BIO工作流程如下:

  1. 服务器端创建一个ServerSocket,并调用accept()方法等待客户端连接。此时线程会被阻塞,直到有客户端连接进来。
  2. 客户端连接成功后,服务器端为该客户端创建一个新的Socket对象,并为其分配一个新的线程来处理该客户端的请求。
  3. 在处理客户端请求时,线程会调用InputStreamOutputStream进行数据的读取或写入操作,这些操作同样会使线程阻塞,直至数据传输完成。
示例代码(Java)
java 复制代码
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(Non-blocking I/O)

概念

NIO即非阻塞式I/O,是Java 1.4引入的新I/O模型。在NIO模型中,线程在进行I/O操作时不会被阻塞,而是可以继续执行其他任务。NIO通过通道(Channel)和缓冲区(Buffer)进行数据传输,使用选择器(Selector)来实现多路复用。

工作流程
  1. 服务器端创建一个ServerSocketChannel,并将其配置为非阻塞模式。
  2. 创建一个Selector,并将ServerSocketChannel注册到Selector上,监听连接事件。
  3. 线程调用Selectorselect()方法,该方法会阻塞,直到有事件发生。
  4. 当有事件发生时,Selector会返回发生事件的通道集合,线程可以遍历这些通道,根据事件类型进行相应的处理。
示例代码(Java)
java 复制代码
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 {
        // 创建ServerSocketChannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(8888));
        serverSocketChannel.configureBlocking(false);

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

        System.out.println("服务器启动,监听端口 8888");

        while (true) {
            // 阻塞等待事件发生
            if (selector.select() == 0) {
                continue;
            }

            // 获取发生事件的SelectionKey集合
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();

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

                if (key.isAcceptable()) {
                    // 处理连接事件
                    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                    SocketChannel socketChannel = ssc.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 len;
                    while ((len = socketChannel.read(buffer)) > 0) {
                        buffer.flip();
                        System.out.println(new String(buffer.array(), 0, len));
                        buffer.clear();
                    }
                }

                // 移除已处理的SelectionKey
                iterator.remove();
            }
        }
    }
}
优缺点
  • 优点:通过多路复用技术,一个线程可以处理多个连接,减少了线程的创建和上下文切换开销,提高了系统的并发处理能力。
  • 缺点:实现相对复杂,需要对通道、缓冲区和选择器等概念有深入的理解。

AIO(Asynchronous I/O)

概念

AIO即异步I/O,是Java 7引入的异步I/O模型。在AIO模型中,当进行I/O操作时,线程不会被阻塞,并且不需要主动去检查操作是否完成。当I/O操作完成后,系统会通过回调函数或Future对象通知线程。

工作流程
  1. 服务器端创建一个AsynchronousServerSocketChannel,并绑定到指定端口。
  2. 调用AsynchronousServerSocketChannelaccept()方法,该方法会立即返回,不会阻塞线程。当有客户端连接进来时,系统会通过回调函数通知线程。
  3. 对于数据的读取和写入操作,同样是异步进行的,操作完成后会通过回调函数通知线程。
示例代码(Java)
java 复制代码
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;
import java.util.concurrent.ExecutionException;

public class AIOServer {
    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {
        // 创建AsynchronousServerSocketChannel
        AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8888));

        System.out.println("服务器启动,监听端口 8888");

        // 接受客户端连接
        serverSocketChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel, Object>() {
            @Override
            public void completed(AsynchronousSocketChannel socketChannel, Object attachment) {
                // 继续接受下一个客户端连接
                serverSocketChannel.accept(null, this);

                try {
                    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();
                                System.out.println(new String(buffer.array(), 0, result));
                            }
                        }

                        @Override
                        public void failed(Throwable exc, ByteBuffer buffer) {
                            exc.printStackTrace();
                        }
                    });
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

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

        // 防止主线程退出
        Thread.sleep(Long.MAX_VALUE);
    }
}
优缺点
  • 优点:真正实现了异步操作,线程在进行I/O操作时不需要等待,提高了系统的性能和响应速度。
  • 缺点:实现复杂度较高,并且对操作系统和硬件有一定的要求。

综上所述,BIO适用于连接数较少且固定的场景;NIO适用于连接数较多但并发量不高的场景;AIO适用于连接数较多且并发量高的场景。

相关推荐
xu_yule37 分钟前
网络和Linux网络-3(套接字编程)TCP网络通信代码
linux·网络·tcp/ip
喜欢吃豆2 小时前
使用 OpenAI Responses API 构建生产级应用的终极指南—— 状态、流式、异步与文件处理
网络·人工智能·自然语言处理·大模型
xixixi777772 小时前
解析一下存储安全——“它是什么”,更是关于“它为何存在”、“如何实现”以及“面临何种挑战与未来”
网络·安全·通信
运维有小邓@2 小时前
实时日志关联分析工具:智能检测潜在安全威胁
运维·网络·安全
j***57683 小时前
电脑可以连接wifi,但是连接后仍然显示没有网络
网络·电脑·php
brave and determined4 小时前
接口通讯学习(day04):RS-232与RS-485:通信接口全解析
网络·uart·通讯·emc·rs232·rs485·嵌入式设计
檀越剑指大厂4 小时前
在家也能远程调代码?WSL+cpolar 的实用技巧分享
网络
秋邱4 小时前
价值升维!公益赋能 + 绿色技术 + 终身学习,构建可持续教育 AI 生态
网络·数据库·人工智能·redis·python·学习·docker
爱学习的大牛1234 小时前
如何系统学习网络渗透测试:从入门到精通的完整指南
网络·学习
程序猿编码4 小时前
PRINCE算法的密码生成器:原理与设计思路(C/C++代码实现)
c语言·网络·c++·算法·安全·prince