IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> BIO、NIO、AIO、多路复用器 -> 正文阅读

[Java知识库]BIO、NIO、AIO、多路复用器

1、BIO(Blocking IO)

1.1 介绍

BIO是同步阻塞IO,当有客户端连接进来时,需要开辟一个线程给这个客户端,所以当连接过高时,会对服务器造成负载过高甚至卡死,

1.2 Java代码示例

public static void main(String[] args) throws Exception {
        ExecutorService threadPool = Executors.newCachedThreadPool();
        ServerSocket ss = new ServerSocket(8888);
        while (true) {
            // 阻塞,等待客户端连接
            Socket client = ss.accept();
            // 连接成功后开辟一个线程
            threadPool.execute(() -> {
                System.out.println(Thread.currentThread().getName() + "成功建立连接....");
                InputStream inputStream;
                try {
                    inputStream = client.getInputStream();
                    BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
                    // 循环接收客户端发来的消息
                    while(true){
                        // 阻塞,等待客户端发送消息
                        String s = br.readLine();
                        if(null == s) {
                            client.close();
                            System.out.print(Thread.currentThread().getName() + "客户端断开连接...");
                            break;
                        }
                        System.out.println(Thread.currentThread().getName() + "客户端发来消息:" + s);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            });
        }
    }

2、NIO(Non-Blocking IO)

2.1 介绍

NIO是同步非阻塞IO,在等待客户端连接时,会有状态返回,状态代表有连接还是没连接,相比BIONIO不需要对每个客户端开辟一个线程进行通信管理,只需要一个线程就能实现的IO模型。在少量的网络连接中NIO可能是个优势,但在大量的网络连接场景中,由于每一次询问channel是否有消息读取时都是一次系统调用,这会造成频繁的用户态到内核态的切换,并且并不是每一个连接都会发生消息,这会导致系统资源占用过多,这就是C10K的问题

2.2 Java代码示例

public static void main(String[] args) throws IOException {
    LinkedList<SocketChannel> clientList = new LinkedList<>();
    ServerSocketChannel ssc = ServerSocketChannel.open();
    ssc.bind(new InetSocketAddress(9999));
    // 设置不阻塞,不设置的话,没有连接就会阻塞,设置为false时没有连接就会返回null
    ssc.configureBlocking(false);
    while (true) {
        SocketChannel accept = ssc.accept();
        if(accept != null) {
            // 等待客户端发送消息为不阻塞
            accept.configureBlocking(false);
            clientList.add(accept);
            System.out.println("客户端" + accept.socket().getPort() + "连接成功");
        }
        // 设置缓冲区容量,接收的对象超过容量就不能再写入了
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
        for(SocketChannel sc : clientList) {
            // 如果不设置accept.configureBlocking(false) 此处会阻塞
            int read = sc.read(byteBuffer);
            if(read > 0) {
                // 翻转缓冲区,在缓冲区写入数据时,缓冲区的position会一直加一,要读取数据则需要将position放到0号位置
                byteBuffer.flip();
                byte[] dataByte = new byte[byteBuffer.limit()];
                byteBuffer.get(dataByte);
                String data = new String(dataByte);
                System.out.println("客户端" + sc.socket().getPort() + "发送消息:" + data);
            } else if(read < 0) {
                clientList.remove(sc);
                System.out.println("客户端" + sc.socket().getPort() + "断开连接");
            }
        }
    }
}

3、AIO

3.1 介绍

AIO是异步非阻塞IO,监听客户端连接请求和消息发送都是基于回调,IO操作充分依赖于操作系统,所以在不同的操作系统或不同的操作系统的内核版本中,性能也不同,在Netty中曾经支持AIO,但后来去除了,因此实际运用中并不广泛,这里不过多介绍

4、多路复用器

4.1 介绍

多路复用器是同步非阻塞,每次有客户端连接时,将fd维护在一个容器中,要获取需要读写的客户端时,只需要将容器中的fd传入内核中完成遍历即可,只需要一次系统调用,有3种实现方式:select、poll、epoll,适用于有大量的网络连接的场景中

4.2 select介绍

select的指令函数如下


int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

nfds:检测前多少个文件描述符,一般是文件描述符最大值+1,因为文件描述符是从0开始

readfds:监控有读数据到达文件描述符的集合

writefds:监控有写数据到达文件描述符的集合

exceptfds:监控有异常的文件描述符集合

timeout:定时阻塞时间,不设置就一直阻塞

其中fd_set最大只能监听1024个fd

4.3 poll介绍

pollselect相比实现方式差不多,但是去掉了监听数量的限制,但同样带来一个问题。每次都需要对所有的fd进行一次线性遍历,时间复杂度为O(n),但并不是所有的fd都有读写数据

4.4 epoll介绍

在Java中默认使用epoll作为多路复用器,epoll的流程:

  1. 先调用epoll_create创建一个红黑树空间并返回一个文件描述符fd6
  2. 当有连接进来时调用epoll_ctl将连接的文件描述符与fd6关联起来
  3. 内核通过中断把有事件的文件描述符copy到一个链表中
  4. 调用epoll_wait获取存放有事件的链表,这个过程无需传入文件描述符,也无需遍历

4.5 Java代码示例

    public static void main(String[] args) throws IOException, InterruptedException {
        Selector selector = Selector.open();
        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.bind(new InetSocketAddress(1000));
        ssc.configureBlocking(false);
        ssc.register(selector, SelectionKey.OP_ACCEPT);
        // 不传值时会一直阻塞
        while (selector.select() > 0) {
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                SelectionKey sk = iterator.next();
                iterator.remove();
                if(sk.isAcceptable()) {
                    ServerSocketChannel channel = (ServerSocketChannel) sk.channel();
                    SocketChannel client = channel.accept();
                    client.configureBlocking(false);
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    client.register(selector, SelectionKey.OP_READ, byteBuffer);
                    System.out.println("客户端" + client.socket().getPort() + "连接成功");
                } else if(sk.isReadable()) {
                    new Thread(() -> {
                        SocketChannel channel = (SocketChannel) sk.channel();
                        ByteBuffer byteBuffer = (ByteBuffer) sk.attachment();
                        try {
                            int read = channel.read(byteBuffer);
                            if (read > 0) {
                                byteBuffer.flip();
                                byte[] bytes = new byte[byteBuffer.limit()];
                                byteBuffer.get(bytes);
                                byteBuffer.clear();
                                System.out.println("客户端" + channel.socket().getPort() + "发送消息:" + new String(bytes));
                            } else if(read < 0) {
                                sk.cancel();
                                channel.close();
                                System.out.println("客户端" + channel.socket().getPort() + "断开连接");
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }).start();
                    Thread.sleep(2000);
                }
            }
        }
    }
  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-04-26 11:28:38  更:2022-04-26 11:30:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 2:41:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码