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)
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)
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)
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适用于连接数较多且并发量高的场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值