跳至主要內容

java IO模型


java IO模型

Java中的AIO、BIO、NIO是三种不同的I/O模型:

  1. BIO(Blocking I/O):同步阻塞I/O模型。在BIO模型中,应用程序发起I/O操作后,会一直阻塞等待I/O操作完成,期间不能进行其它任务。当I/O操作完成后,应用程序才能继续执行。BIO模型是Java早期的I/O API,使用了传统的同步I/O机制。BIO 是传统的阻塞式 I/O 模型,它以流的形式进行操作,每个连接都需要独立的线程来处理,当一个线程进行 I/O 操作时,它会一直阻塞直到数据准备好,这样就会造成线程数量的浪费。BIO 适用于连接数比较小的情况。
  2. NIO(Non-blocking I/O):同步非阻塞I/O模型。在NIO模型中,应用程序发起I/O操作后,不需要等待I/O操作完成,可以继续执行其它任务。但是在检查I/O操作完成状态时,仍然需要进行阻塞等待,直到I/O操作完成或者有数据可读取。NIO模型是Java 1.4中引入的新I/O API,使用了基于事件驱动的I/O机制。NIO 是一种基于事件驱动的 I/O 模型,它通过一个线程来处理多个连接,这样可以避免了线程数量的浪费。当一个连接有数据可读时,会触发一个事件通知线程,线程就可以处理该连接的数据。NIO 适用于连接数较多的情况。
  3. AIO(Asynchronous I/O):异步非阻塞I/O模型。在AIO模型中,应用程序发起I/O操作后,不需要等待I/O操作完成,而是可以继续执行其它任务。当I/O操作完成后,操作系统会通知应用程序进行后续处理。AIO模型是Java 7中引入的新I/O API,使用了操作系统的异步I/O机制。AIO 是 Java 7 中引入的一种异步 I/O 模型,它是基于 NIO 的,但是相比于 NIO,AIO 更加高级,可以实现异步读写操作。当一个 I/O 操作完成时,会触发一个回调函数,这样就可以实现异步处理。AIO 适用于连接数极多、连接时间较长、但数据交互量较小的情况。

相比于BIO模型,AIO和NIO模型能够支持更高的并发性能和更低的系统开销,因为它们能够利用操作系统的异步I/O机制或事件驱动机制,让应用程序更有效地利用CPU资源和I/O资源。但是,AIO和NIO模型的编程模型比BIO模型更复杂,需要更加深入的理解和掌握。

总之,BIO 适用于连接数比较小的情况,NIO 适用于连接数较多的情况,而 AIO 适用于连接数极多、连接时间较长、但数据交互量较小的情况。在实际应用中,应根据具体业务场景选择合适的 I/O 模型。

BIO

BIO介绍

同步阻塞I/O模型。在BIO模型中,应用程序发起I/O操作后,会一直阻塞等待I/O操作完成,期间不能进行其它任务。当I/O操作完成后,应用程序才能继续执行。BIO模型是Java早期的I/O API,使用了传统的同步I/O机制。

BIO 是传统的阻塞式 I/O 模型,它以流的形式进行操作,每个连接都需要独立的线程来处理,当一个线程进行 I/O 操作时,它会一直阻塞直到数据准备好,这样就会造成线程数量的浪费。BIO 适用于连接数比较小的情况。

BIO示例

import java.io.*;
import java.net.*;

public class BIOExample {
    public static void main(String[] args) throws IOException {
        // 创建ServerSocket对象,监听端口号为8888
        ServerSocket serverSocket = new ServerSocket(8888);
        System.out.println("Server started on port 8888.");

        while (true) {
            // 等待客户端连接
            Socket clientSocket = serverSocket.accept();
            System.out.println("Client connected from " + clientSocket.getRemoteSocketAddress());

            // 从客户端读取数据
            BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            String message = in.readLine();
            System.out.println("Received message from client: " + message);

            // 发送响应数据给客户端
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
            out.println("Hello, client!");

            // 关闭连接
            clientSocket.close();
        }
    }
}

这是一个BIO例子,其中:

  • ServerSocket对象用于创建服务器Socket,监听端口号为8888;
  • 通过serverSocket.accept()方法阻塞等待客户端连接;
  • 从客户端读取数据使用BufferedReaderInputStreamReader,发送响应数据给客户端使用PrintWriter
  • 最后关闭连接。

NIO

NIO介绍

同步非阻塞I/O模型。在NIO模型中,应用程序发起I/O操作后,不需要等待I/O操作完成,可以继续执行其它任务。但是在检查I/O操作完成状态时,仍然需要进行阻塞等待,直到I/O操作完成或者有数据可读取。NIO模型是Java 1.4中引入的新I/O API,使用了基于事件驱动的I/O机制。

NIO 是一种基于事件驱动的 I/O 模型,它通过一个线程来处理多个连接,这样可以避免了线程数量的浪费。当一个连接有数据可读时,会触发一个事件通知线程,线程就可以处理该连接的数据。NIO 适用于连接数较多的情况。

NIO示例

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;

public class NIOExample {
    public static void main(String[] args) throws IOException {
        // 创建ServerSocketChannel对象,监听端口号为8888
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.bind(new InetSocketAddress(8888));
        serverSocketChannel.configureBlocking(false);
        System.out.println("Server started on port 8888.");

        // 创建Selector对象,用于监听事件
        Selector selector = Selector.open();
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            // 等待事件发生
            selector.select();
            for (SelectionKey key : selector.selectedKeys()) {
                if (key.isAcceptable()) {
                    // 接受客户端连接
                    ServerSocketChannel serverChannel = (ServerSocketChannel) key.channel();
                    SocketChannel clientChannel = serverChannel.accept();
                    clientChannel.configureBlocking(false);
                    clientChannel.register(selector, SelectionKey.OP_READ);
                    System.out.println("Client connected from " + clientChannel.getRemoteAddress());
                } else if (key.isReadable()) {
                    // 读取客户端数据
                    SocketChannel clientChannel = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int bytesRead = clientChannel.read(buffer);
                    if (bytesRead > 0) {
                        buffer.flip();
                        byte[] bytes = new byte[buffer.remaining()];
                        buffer.get(bytes);
                        String message = new String(bytes, "UTF-8");
                        System.out.println("Received message from client: " + message);

                        // 发送响应数据给客户端
                        ByteBuffer responseBuffer = ByteBuffer.wrap("Hello, client!".getBytes("UTF-8"));
                        clientChannel.write(responseBuffer);
                    } else if (bytesRead < 0) {
                        clientChannel.close();
                    }
                }
            }
            selector.selectedKeys().clear();
        }
    }
}

这是一个NIO例子,其中:

  • ServerSocketChannel对象用于创建服务器Socket,监听端口号为8888;
  • 通过Selector对象监听事件;
  • 使用SelectionKey类来表示事件,包括OP_ACCEPT和OP_READ;
  • 在OP_ACCEPT事件中,接受客户端连接,并将客户端的SocketChannel注册到Selector中以监听读事件;
  • 在OP_READ事件中,读取客户端数据,发送响应数据给客户端。

AIO

AIO介绍

异步非阻塞I/O模型。在AIO模型中,应用程序发起I/O操作后,不需要等待I/O操作完成,而是可以继续执行其它任务。当I/O操作完成后,操作系统会通知应用程序进行后续处理。AIO模型是Java 7中引入的新I/O API,使用了操作系统的异步I/O机制。

AIO 是 Java 7 中引入的一种异步 I/O 模型,它是基于 NIO 的,但是相比于 NIO,AIO 更加高级,可以实现异步读写操作。当一个 I/O 操作完成时,会触发一个回调函数,这样就可以实现异步处理。AIO 适用于连接数极多、连接时间较长、但数据交互量较小的情况。

AIO示例

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class AIOExample {
    public static void main(String[] args) throws IOException {
        // 创建AsynchronousServerSocketChannel对象,监听端口号为8888
        AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open();
        serverChannel.bind(new InetSocketAddress(8888));
        System.out.println("Server started on port 8888.");

        // 注册Accept事件
        serverChannel.accept(null, new CompletionHandler<AsynchronousSocketChannel,IOException>() {
            @Override
            public void completed(AsynchronousSocketChannel clientChannel, Object attachment) {
                // 接受客户端连接成功,注册Read事件
                serverChannel.accept(null, this);
                ByteBuffer buffer = ByteBuffer.allocate(1024);
                clientChannel.read(buffer, buffer, new CompletionHandler<Integer, ByteBuffer>() {
                    @Override
                    public void completed(Integer result, ByteBuffer attachment) {
                        if (result > 0) {
                            attachment.flip();
                            byte[] bytes = new byte[attachment.remaining()];
                            attachment.get(bytes);
                            String message = new String(bytes, "UTF-8");
                            System.out.println("Received message from client: " + message);

                            // 发送响应数据给客户端
                            ByteBuffer responseBuffer = ByteBuffer.wrap("Hello, client!".getBytes("UTF-8"));
                            clientChannel.write(responseBuffer, responseBuffer, new CompletionHandler<Integer, ByteBuffer>() {
                                @Override
                                public void completed(Integer result, ByteBuffer attachment) {
                                    if (attachment.hasRemaining()) {
                                        clientChannel.write(attachment, attachment, this);
                                    }
                                }

                                @Override
                                public void failed(Throwable exc, ByteBuffer attachment) {
                                    System.out.println("Failed to send response to client.");
                                }
                            });
                        } else if (result < 0) {
                            try {
                                clientChannel.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
                    }

                    @Override
                    public void failed(Throwable exc, ByteBuffer attachment) {
                        System.out.println("Failed to read data from client.");
                    }
                });
            }

            @Override
            public void failed(Throwable exc, Object attachment) {
                System.out.println("Failed to accept client connection.");
            }
        });

        // 阻塞主线程
        try {
            Thread.currentThread().join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

这是一个AIO例子,其中:

  • AsynchronousServerSocketChannel对象用于创建服务器Socket,监听端口号为8888;
  • 使用CompletionHandler回调函数来处理事件,包括Accept和Read事件;
  • 在Accept事件中,接受客户端连接,并将客户端的AsynchronousSocketChannel注册到Selector中以监听读事件;
  • 在Read事件中,读取客户端数据,发送响应数据给客户端。
上次编辑于:
贡献者: Neil