【java】BIO,NIO,多路IO复用,AIO

在Java中,处理I/O操作的模型主要有四种:阻塞I/O (BIO), 非阻塞I/O (NIO), 异步I/O (AIO), 以及IO多路复用。下面详细介绍这四种I/O模型的工作原理和应用场景。

1. 阻塞I/O (BIO)

工作原理

阻塞I/O是最传统的I/O模型。在这种模型中,当一个线程发起一个I/O请求(如读写操作)时,该线程会被阻塞,直到I/O操作完成。这意味着线程必须等待I/O操作完成才能继续执行。

代码示例
java 复制代码
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;

public class BioServer {
    public static void main(String[] args) throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);
        System.out.println("Server started on port 8080");

        while (true) {
            Socket clientSocket = serverSocket.accept(); // 阻塞等待客户端连接
            new Thread(() -> {
                try (BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) {
                    String line;
                    while ((line = reader.readLine()) != null) {
                        System.out.println("Received: " + line);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}
优点
  • 实现简单。
缺点
  • 每个连接都需要一个线程来处理,当并发连接数增加时,线程的数量也会增加,可能导致系统资源耗尽。

2. 非阻塞I/O (NIO)

工作原理

非阻塞I/O模型允许线程在发起I/O请求时不会被阻塞,如果数据不可用或设备忙,则立即返回一个错误或特殊值。线程可以选择立即再次尝试I/O操作或去做其他事情,从而提高了CPU的利用率。

代码示例
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 {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(8080));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

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

            while (keyIterator.hasNext()) {
                SelectionKey key = keyIterator.next();
                if (key.isAcceptable()) {
                    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                    SocketChannel sc = ssc.accept();
                    sc.configureBlocking(false);
                    sc.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    SocketChannel sc = (SocketChannel) key.channel();
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    int readBytes = sc.read(buffer);
                    if (readBytes > 0) {
                        buffer.flip();
                        byte[] data = new byte[buffer.remaining()];
                        buffer.get(data);
                        System.out.println("Received: " + new String(data));
                    }
                }
                keyIterator.remove();
            }
        }
    }
}
优点
  • 提高了单个线程处理多个连接的能力,降低了系统资源消耗。
  • 可以处理大量并发连接。
缺点
  • 实现相对复杂。
  • 需要手动管理缓冲区、选择器等。

3. IO多路复用

工作原理

IO多路复用允许一个进程同时监听多个文件描述符(例如socket),并只在某个描述符准备好进行读写操作时才进行处理。常用的多路复用机制有selectpollepoll。这种模型非常适合处理大量并发连接的场景。

代码示例
java 复制代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class SelectServer {
    public static void main(String[] args) throws IOException {
        Selector selector = Selector.open();
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        serverSocketChannel.socket().bind(new InetSocketAddress(8080));
        serverSocketChannel.configureBlocking(false);
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            selector.select();
            for (SelectionKey key : selector.selectedKeys()) {
                if (key.isAcceptable()) {
                    ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                    SocketChannel sc = ssc.accept();
                    sc.configureBlocking(false);
                    sc.register(selector, SelectionKey.OP_READ);
                } else if (key.isReadable()) {
                    SocketChannel sc = (SocketChannel) key.channel();
                    // 读取数据...
                }
            }
            selector.selectedKeys().clear();
        }
    }
}
优点
  • 可以同时监听多个文件描述符,提高处理大量并发连接的能力。
  • 提高了资源利用率。
缺点
  • 在Java中,selectpoll的性能不如epoll,后者仅在Linux系统中可用。

4. 异步I/O (AIO)

工作原理

异步I/O是真正的异步操作模型,进程发起I/O请求后可以立即返回并继续执行其他任务,而无需等待I/O操作完成。当I/O操作完成后,操作系统会通知进程结果。

代码示例
java 复制代码
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.CountDownLatch;

public class AioServer {
    private static final CountDownLatch latch = new CountDownLatch(1);

    public static void main(String[] args) throws IOException, InterruptedException {
        AsynchronousServerSocketChannel server = AsynchronousServerSocketChannel.open().bind(new java.net.InetSocketAddress(8080));
        server.accept(null, new AcceptHandler(server));
        latch.await();
    }

    static class AcceptHandler implements CompletionHandler<AsynchronousSocketChannel, Object> {
        private AsynchronousServerSocketChannel server;

        public AcceptHandler(AsynchronousServerSocketChannel server) {
            this.server = server;
        }

        @Override
        public void completed(AsynchronousSocketChannel result, Object attachment) {
            result.read(ByteBuffer.allocate(1024), null, new ReadHandler(result));
            server.accept(null, this);
        }

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

    static class ReadHandler implements CompletionHandler<Integer, Object> {
        private AsynchronousSocketChannel channel;

        public ReadHandler(AsynchronousSocketChannel channel) {
            this.channel = channel;
        }

        @Override
        public void completed(Integer result, Object attachment) {
            ByteBuffer buffer = (ByteBuffer) attachment;
            buffer.flip();
            byte[] data = new byte[buffer.remaining()];
            buffer.get(data);
            System.out.println("Received: " + new String(data));
            channel.close();
        }

        @Override
        public void failed(Throwable exc, Object attachment) {
            exc.printStackTrace();
            try {
                ((AsynchronousSocketChannel) attachment).close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
优点
  • 真正的异步操作,提高了系统的并发能力和响应速度。
  • 适用于高并发场景。
缺点
  • 实现较为复杂。
  • Java中AIO的支持相对较少,不如NIO成熟。

总结

  • BIO:适合连接数较少的场景。
  • NIO:适用于中等并发的场景,提高了资源利用率。
  • IO多路复用:适合大量并发连接的场景,特别是在服务器端。
  • AIO:适用于高并发场景,真正实现了异步操作。

选择哪种模型取决于具体的应用场景和需求。例如,对于需要处理大量并发连接的服务器,IO多路复用和异步I/O可能是更佳的选择。而对于简单的、单线程的应用,阻塞I/O可能就已经足够。

相关推荐
计算机毕设指导66 分钟前
基于 SpringBoot 的作业管理系统【附源码】
java·vue.js·spring boot·后端·mysql·spring·intellij-idea
Gu Gu Study7 分钟前
枚举与lambda表达式,枚举实现单例模式为什么是安全的,lambda表达式与函数式接口的小九九~
java·开发语言
Chris _data10 分钟前
二叉树oj题解析
java·数据结构
牙牙70515 分钟前
Centos7安装Jenkins脚本一键部署
java·servlet·jenkins
paopaokaka_luck23 分钟前
[371]基于springboot的高校实习管理系统
java·spring boot·后端
以后不吃煲仔饭36 分钟前
Java基础夯实——2.7 线程上下文切换
java·开发语言
进阶的架构师36 分钟前
2024年Java面试题及答案整理(1000+面试题附答案解析)
java·开发语言
The_Ticker42 分钟前
CFD平台如何接入实时行情源
java·大数据·数据库·人工智能·算法·区块链·软件工程
傻啦嘿哟1 小时前
如何使用 Python 开发一个简单的文本数据转换为 Excel 工具
开发语言·python·excel
大数据编程之光1 小时前
Flink Standalone集群模式安装部署全攻略
java·大数据·开发语言·面试·flink