BIO、NIO、AIO理解

2023-11-19

一、到底什么是BIO、NIO、AIO?

这些可以理解为是Java语言对操作系统的各种IO模型的封装,程序员在使用这些API的时候,不需要关系操作系统层面的知识,也不需要根据不同操作系统编写不同的代码,只需要使用Java的API就可以了。

二、BIO、NIO、AIO的区别

1.BIO就是传统的java.io包(意思就是你在使用java.io包进行输入输出操作的时候,就是使用的BIO通信机制),BIO是传统的同步阻塞式的I/O。也就是说在读入输入流或者输出流时,在读写操作完成之前,线程会一直阻塞,会一直占用CPU资源,直到读写操作完成之后,才继续完成下面的任务。

采用BIO通信模型的服务端,通常有一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端的连接请求之后,会为客户端每一个连接请求创建一个新的线程进行处理,处理完之后,通过输出流返回给客户端,线程销毁,这就是典型的一请求一应答模型。

 可以看出,这是在多线程情况下执行的。当在单线程环境条件下,在while循环中服务端会调用accept方法等待接收客户端的连接请求,一旦收到这个连接请求,就可以建立socket,并在socket上进行读写操作,此时不能再接收其他客户端的连接请求,只能等待同当前服务端连接的客户端的操作完成或者连接断开。

该模型最大的缺单就是缺乏弹性伸缩能力,当客户端并发访问量增加后,服务端的线程个数和客户端并发访问数呈1:1的正比关系,由于线程是java虚拟机非常宝贵的系统资源,当线程数膨胀之后,系统的性能会急剧下降,随着并发量的继续增大,系统会发生线程堆栈溢出、创建新线程失败等问题,并最终导致进程宕机或者僵死,不能对外提供服务。

BIO实现的代码:

public class server {
    private static Socket socket=null;
    public static void main(String[] args) {
        try {
            //绑定端口
            ServerSocket serverSocket=new ServerSocket();
            serverSocket.bind(new InetSocketAddress(8080));
            while (true){
                //等待连接  阻塞
                System.out.println("等待连接");
                socket = serverSocket.accept();
                System.out.println("连接成功");
                //连接成功后新开一个线程去处理这个连接
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        byte[] bytes=new byte[1024];
                        try {
                            System.out.println("等待读取数据");
                            //等待读取数据    阻塞
                            int length=socket.getInputStream().read(bytes);
                            System.out.println(new String(bytes,0,length));
                            System.out.println("数据读取成功");
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

2.NIO是Java 1.4引入的java.nio包,提供了Channel、Selector、Buffer等新的抽象,可以构建多路复用的、同步非阻塞式IO程序,同时提供了更接近操作系统底层高性能的数据操作方式。NIO通过使用单线程轮询多个连接的的方式来实现高效的处理方式,可以支持较大数量的并发连接,但编程模型较为复杂。

NIO是为了解决BIO的缺陷提出的通信模型,以socket.read()为例:

传统的BIO里面的socket.read(),如果TCP RecvBuffer里没有数据,函数会一直阻塞,直到收到数据。

对于NIO,如果TCP RecvBuffer有数据,就把数据从网卡读到内存,并且返回给用户;反之则直接返回0,永远不会阻塞。

BIO关心的是“我要读”的问题,NIO关心的是“我可以读了”的问题。

NIO一个重要的特点是:socket主要的读、写、注册和接收函数,在等待就绪阶段都是非阻塞的,真正的I/O操作是同步阻塞的(消耗CPU但性能非常高)。

2.1 什么是Channel?

Channel,翻译过来就是“通道”,就是数据传输的管道,类似于“流”,但是与“流”又有着区别。

Channel与“流”的区别:

  • 既可以从Channel中读取数据,又可以写数据到Channel,但流的读写通常是单向的——输入输出流
  • 通道可以异步读写
  • 通道中的数据总是先读取到buffer(缓冲区),或者总是需要从一个buffer写入,不能直接访问数据

2.2 什么是Buffer?

Buffer是一个对象,里面是要写入或者读出的数据,在java.nio库中,所有的数据都是用缓冲区处理的。

在读取数据时,它是直接读到缓冲区中的;在写入数据时,也是直接写到缓冲区中,任何时候访问Channel中的数据,都是通过缓冲区进行操作的。

缓冲区实质上是一个数组,通常是一个字节数组ByteBuffer,当然也有其他类型的:

2.3 什么是Selector? 

Selector被称为选择器,Selector会不断地轮询注册在其上的Channel,如果某个Channel上发生读或写事件,这个Channel就被判定处于就绪状态,会被Selector轮询出来,然后通过SelectionKey可以获取到就绪Channel的集合,进行后续的I/O操作。

一个多路复用器Selector可以同时轮询多个Channel,JDK使用了epoll()代替了传统的select实现,所以并没有最大连接句柄的限制,这意味着只需要一个线程负责Selector的轮询,就可以接入成千上万的客户端。

2.4 Buffer、Selector、Channel之间的关系?

 2.5 NIO多路复用的实现

NIO是利用了单线程轮询事件的机制,通过高效地定位就绪的Channel,来决定做什么,仅仅select阶段是阻塞的,可以有效避免大量客户端连接时,频繁切换线程带来的问题,应用的拓展能力有了很大的提高。

  • 首先,通过Selector.open()创建一个Selector,作为类似调度员的角色;
  • 然后,创建一个ServerSocketChannel,并在Selector中注册这个Channel,通过指定的SelectionKey.OP_ACCEPT,告诉调度员,它关注的是新的连接请求;
  • 为什么我们要明确配置非阻塞模式呢?这是因为阻塞模式下,注册模式是不允许的,会抛出IIIegalBlockingModeException异常;
  • Selector阻塞在select操作,当有Channel发生接入请求,就会被唤醒;
public class server {
    public static void main(String[] args) {
        try {
            //创建一个socket通道,并且设置为非阻塞的方式
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);//设置为非阻塞的方式
            serverSocketChannel.socket().bind(new InetSocketAddress(9000));
            //创建一个selector选择器,把channel注册到selector选择器上
            Selector selector = Selector.open();
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
            while(true)
            {
                System.out.println("等待事件发生");
                selector.select();
                System.out.println("有事件发生了");
                Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
                while(iterator.hasNext())
                {
                    SelectionKey key = iterator.next();
                    iterator.remove();
                    handle(key);
                }

            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static void handle(SelectionKey key) throws IOException{
        if(key.isAcceptable())
        {
            System.out.println("连接事件发生");
            ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
            //创建客户端一侧的channel,并注册到selector上
            SocketChannel socketChannel = serverSocketChannel.accept();
            socketChannel.configureBlocking(false);
            socketChannel.register(key.selector(),SelectionKey.OP_READ);
        } else if (key.isReadable()) {
            System.out.println("数据可读事件发生");
            SocketChannel socketChannel = (SocketChannel) key.channel();
            ByteBuffer buffer = ByteBuffer.allocate(1024);
            int len = socketChannel.read(buffer);
            if(len!=-1)
            {
                System.out.println("读取到客户端发送的数据:"+new String(buffer.array(),0,len));
            }
            //给客户端发送信息
            ByteBuffer wrap = ByteBuffer.wrap("hello world".getBytes());
            socketChannel.write(wrap);
            key.interestOps(SelectionKey.OP_READ|SelectionKey.OP_WRITE);
            socketChannel.close();
        }
    }

下面是多路复用的流程图:

 3.AIO是Java 1.7之后引入的包,是NIO的升级版本。异步非阻塞I/O,通过回调的方式实现高效的I/O操作,也就是应用操作之后会直接返回,不会堵塞(此时用户进程只需要对数据处理,而不需要进行实际的IO读写操作,因为真正的IO操作已经由操作系统内核完成了),当后台去处理完成之后,操作系统会通知相应的线程进行后续的操作。这样可以大大降低系统资源的消耗,适用于高并发、高吞吐量的场景,但在实际使用中可能会受到操作系统和硬件的限制。

4.可以用一个例子来描述这三个通信模型的区别:设想你要烧水这样一个场景

BIO:在烧水期间,你一直守在旁边,不敢任何事,等到水烧开了才去完成其他事

NIO:在烧水期间,你不必一直守在旁边,而是时不时来看水是否烧开,其他时间可以去完成其他事

AIO:在烧水期间,你无需来看水是否烧开了,可以去完成其他任务,水烧开的时候会发出提示音提示你水开了,这时候你再来处理。

三、一些基本概念的补充

同步和异步(线程之间的调用):

同步操作时,调用者需要等待被调用者返回结果,才会进行下一步操作

而异步则相反,调用者不需要等待被调用者返回调用,即可进行下一步操作,被调用者通常依靠事件、回调等机制来通知调用者返回调用结果

阻塞和非阻塞(线程内调用):

阻塞和非阻塞是对同一个线程来说的,在某个时刻,线程要么处于阻塞,要么处于非阻塞

阻塞和非阻塞关注的是程序在等待调用结果(消息、返回值)时的状态:

阻塞调用是指在调用结果返回之前,当前线程会被挂起,调用线程只有在得到结果之后才会返回

非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程

IO分为两个操作:第一步是发起IO请求,第二步真正执行IO读写操作

同步和异步IO的概念:

同步是用户线程发起IO请求之后一直等待或者轮询内核IO操作完成之后才能继续执行后续代码。

异步是用户线程发起IO请求后仍然继续执行后续代码,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数

阻塞和非阻塞IO的概念:

阻塞是指IO读写操作需要彻底完成后才能返回用户空间

非阻塞是指IO读写操作被调用后立即返回一个状态值,无需等待IO操作彻底完成

四种组合方式:

参考资料:

Java NIO三大角色Channel、Buffer、Selector相关解析 - 掘金 (juejin.cn)https://juejin.cn/post/7037028848487104519#heading-28Java NIO 中的 Channel 详解 - 掘金 (juejin.cn)https://juejin.cn/post/7058948260529963039JAVA中BIO、NIO、AIO的分析理解-阿里云开发者社区 (aliyun.com)https://developer.aliyun.com/article/726698#slide-26Java核心(五)深入理解BIO、NIO、AIO - 腾讯云开发者社区-腾讯云 (tencent.com)https://cloud.tencent.com/developer/article/1376675Java NIO浅析 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/23488863彻底理解同步 异步 阻塞 非阻塞 - LittleDonkey - 博客园 (cnblogs.com)https://www.cnblogs.com/loveer/p/11479249.html

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

BIO、NIO、AIO理解 的相关文章

  • 高并发 Apache 异步 HTTP 客户端 IOReactor 问题

    应用说明 我正在使用由 Comsat 的 Quasar FiberHttpClient 版本 0 7 0 包装的 Apache HTTP 异步客户端 版本 4 1 1 来运行和执行高度并发的 Java 应用程序 该应用程序使用光纤在内部将
  • 基于信号完整性的PCB设计原则

    最小化单根信号线质量的一些PCB设计建议 1 使用受控阻抗线 2 理想情况下 所有信号都应该使用完整的电源或地平面作为其返回路径 关键信号则使用地平面作为返回路径 3 信号的返回参考面发生变化时 在尽可能接近信号换层孔的位置增加至少一个返回
  • 2024诸多大厂春招提前启动!Android的程序员还在等什么

    春招 提前批 已开 xdm别打瞌睡了 格力 顺丰 酷狗 沃尔玛中国 理想 科大讯飞等开启春招 开始收简历了 还有hc的企业提前抢人 春招时间短 节奏快 招满即止 就算挂了也绝不能不投 对企业来说 秋招和春招都是储备人才的黄金时期 春招中 除
  • 驾驭远程工作:提高工作效率与灵活性的秘诀

    随着科技的飞速发展 远程工作已成为越来越多企业和员工的选择 这种工作模式不仅为员工提供了更大的灵活性 也为提高工作效率创造了新的可能 本文将深入探讨如何通过远程工作提高工作效率和灵活性 一 明确目标与计划 在远程工作中 明确的目标和计划至关
  • 挖掘知识的宝藏:如何利用在线资源提升个人技能

    在这个信息爆炸的时代 互联网已经成为我们获取知识 提升技能的重要途径 无论是学习编程 提高语言能力 还是了解新的行业趋势 网络资源都为我们提供了无限可能 本文将探讨如何有效利用在线资源进行自我提升 一 选择合适的在线学习平台 首先 我们需要
  • 会议设备:提升会议体验与效率的关键

    在当今高度信息化的社会 会议已成为企业 机构和团队之间交流与合作的重要方式 而会议设备的选择与使用 对于提升会议的体验与效率具有举足轻重的地位 本文将详细探讨会议设备的重要性 以及如何选择和使用合适的会议设备 以实现高效 顺畅的沟通 首先
  • 数据库 | 面试官:一次到底插入多少条数据合适啊?.....面试连环炮

    数据库 面试官 一次到底插入多少条数据合适啊 面试连环炮 数据库插入操作的基础知识 插入数据是数据库操作中的基础 但是 我们程序员将面临随之而来的问题 如何快速有效地插入数据 并保持数据库 性能 当你向数据库中插入数据时 这些数据直接存储到
  • 像素高低:影响照片质量、分辨率与细节表现的奥秘

    在数字摄影时代 像素成为了衡量照片质量的重要标准之一 那么 什么是像素 像素的高低又如何影响照片的质量 分辨率和细节表现呢 本文将为您揭开这个奥秘 首先 我们来了解一下像素的基本概念 像素 Pixel 是组成数字图像的基本单元 通常由字母
  • 哪里有视频压缩软件免费版在线使用?轻松压缩视频大小

    作为一个文件管理员 我常常需要处理大量的视频文件 有时候 视频文件过大给传输 存储带来了诸多不便 遇到这种情况 很多人就会选择使用视频压缩软件来减小文件大小 不过一些小伙伴不清楚视频压缩软件app 电脑软件 网页有哪些 接下来我们将从功能
  • AI新年头像模板在哪找?告别单调头像的工具分享

    农历新年到啦 今年的春节假期你有没有弄一些氛围装饰呀 除了每年都有的对联 福字等 经典皮肤 现在也流行起给自己的账号营造一些过年的感觉啦 说到这个 很多人第一反应就是给自己换个新年头像 限定皮肤 不过头像要怎么营造新年的感觉呢 其实用AI工
  • Java中如何实现并发读取映射到内存的文件?

    我有很多线程同时读取同一个文件 总共大约100M 并且只有一个线程更新文件 我想将文件映射到内存中以减少FILE I O 在 Java 中如何做到这一点 我基本上考虑了以下2种方法 用字节数组存储文件 多线程读取时每次创建ByteArray
  • 为什么 Files.list() 并行流的执行速度比使用 Collection.parallelStream() 慢得多?

    以下代码片段是获取目录列表 对每个文件调用提取方法并将生成的药物对象序列化为 xml 的方法的一部分 try Stream
  • 哪些文件系统支持 Java UserDefinedFileAttributeView?

    我需要用文件系统中的文件存储自定义数据 每个文件大约 50 个字节 我没有任何其他存储来保存数据 并且无法为此创建额外的文件 这些是我的要求我无法改变它 我发现这可以使用 UserDefinedFileAttributeView 类来完成
  • Java NIO ByteBuffer,翻转后写入

    我是 Java ByteBuffers 的新手 想知道翻转后写入 ByteBuffer 的正确方法是什么 在我的用例中 我将一个输出缓冲区写入套接字 outBuffer flip Non blocking SocketChannel int
  • 尽管通道已准备就绪,Java NIO 选择器 select() 返回 0

    我的 Java NIO 选择器是使用实现的select http docs oracle com javase 6 docs api java nio channels Selector html select 28 29所以它会阻塞 直到
  • 具有 AsynchronousServerSocketChannel 的多线程服务器

    我必须实现一个应该接受更多连接的服务器 没有任何更深入的思考 我决定使用新的 JAVA NIO 2 类 我目前的做法是 final Semaphore wait new Semaphore 1 while true wait acquire
  • Jetty 和其他容器如何在遵守 Servlet 规范的同时利用 NIO?

    我是 NIO 的新手 我正在尝试弄清楚 Jetty 如何利用 NIO 我对使用 Blocking IO 的传统 servlet 容器如何服务请求的理解如下 请求到达 分配一个线程来处理请求和 servlet 方法 doGet等 被调用 Se
  • 在 zip 中写入(修改或添加)文件

    我已按照中的说明进行操作这个线程 https stackoverflow com questions 13787318 java util zip replace a single zip file 使用其中的代码 我已经能够将文件添加到
  • Java 中排序(内存映射?)文件中的二分搜索

    我正在努力将 Perl 程序移植到 Java 并一边学习 Java 原始程序的核心组成部分是Perl模块 http search cpan org jfreeman File SortedSeek 0 015 lib File Sorted
  • Java 7 watchservice获取文件更改偏移量

    我刚刚尝试使用 Java 7 WatchService 来监视文件的更改 这是我敲出的一些代码 WatchService watcher FileSystems getDefault newWatchService Path path Pa

随机推荐

  • 1+x 证书 Web 前端开发中级理论考试(试卷 6 )

    1 x 证书 Web 前端开发中级理论考试 试卷 6 官方QQ群 1 x 证书 web 前端开发初级对应课程分析 http blog zh66 club index php archives 194 1 X 证书 Web 前端开发中级对应课
  • 2022国赛5:神州路由器DHCP服务配置

    配置如下 RA配置 Router conf Router host RA RA config int g0 1 RA config g0 1 ip add 192 168 10 1 255 255 255 0 RA config g0 1
  • STM32+ESP8266+MQTT连接阿里云(1)

    ESP8266连接阿里云的流程 发送 目的是让ESP8266退出透传 AT RESTORE 让模块恢复出厂设置 AT 判断模块的好坏及工作状态 正常就会回复OK ATE0 关闭回显 这个没什么好说的 AT CWMODE CUR 1 设置为s
  • 【网络是怎样连接的】—— 向 DNS 服务器查询 IP 地址

    IP 1 基本知识 互联网和公司内部的局域网都是基于 TCP IP 的思路来设计的 由一些小的子网 通过路由器连接起来组成一个大的网络 这里的子网可以理解为用集线器连接起来的几台计算机 在网络中 所有的设备都会被分配一个地址 这个地址就相当
  • 设计模式学习笔记(一)之单例模式

    单例模式 作用 保证一个类只有一个实例 并且提供访问这个实例的全局访问点 应用场景有 数据库连接池 spring中 Bean默认是单例 Servlet中 每一个Servlet是单例 配置文件的类 一般是单例 优点 单例只生成一个实例 减少系
  • UVM实战——01基本概念_2 什么是UVM?

    什么是UVM 1 什么是UVM 2 UVM的特点 3 UVM提供的资源 3 1 编程指导 3 1 1 理念 3 1 2 功能 3 2 验证组件 3 3 验证激励 3 4 通信机制 3 5 宏 1 什么是UVM UVM Universal V
  • 在iOS9上调用支付宝不回调的问题解决,以及支付宝嵌入的流程梳理

    又有一段时间没有经营自己的博客了 这一段有点忙啊 在最近的一个项目中再一次用到了第三方支付 对 就是支付宝 之前的项目其实已经实现过相应的功能 那是还是在ios8的系统下 这不在iOS9下就遇到了一个问题 不回调啊 反正要梳理支付宝的嵌入
  • 搭建树莓派Pico交叉编译环境和工具链(arm-none-eabi-gcc)时可能会遇到的错误以及解决方案

    本文是一个类似手册的文章 用来记录可能遇到的错误 你可以通过侧栏选择遇到的错误来查看详细信息 No install step for ELF2UF2Build 遇到这种错误有两种原因 安装了版本不对或者不完整的arm none eabi g
  • 继电器、并联的二极管和驱动三极管选型实战演练

    继电器选型原则 继电器的选用原则参见下表 在表中 必须确定 栏中有 号的项目被确定之后 就可选定一款继电器 如果有进一步的要求 需要进一步考虑 参考 栏中有 号的相应项目 下面对表格中的所有参数进行详细说明 触点 1触点负载 确定继电器所能
  • 一篇文章了解爬虫技术现状

    本文全面的分析了爬虫的原理 技术现状 以及目前仍面临的问题 如果你没接触过爬虫 本文很适合你 如果你是一名资深的虫师 那么文末的彩蛋你可能感兴趣 需求 万维网上有着无数的网页 包含着海量的信息 无孔不入 森罗万象 但很多时候 无论出于数据分
  • list【2】模拟实现(含迭代器实现超详解哦)

    模拟实现list 引言 实现概述 list迭代器实现 默认成员函数 operator 与 operator gt operator 与 operator operator 与 operator 迭代器实现概览 list主要接口实现 默认成员
  • pnpm修改设置下载包的存储路径

    要修改 pnpm 存储依赖的路径 可以使用 pnpm 的 store 配置选项 通过更改 store 配置 可以指定 pnpm 存储依赖的目录位置 这在希望将依赖存储在不同磁盘分区 不同的硬盘驱动器或其他自定义位置时很有用 步骤 1 打开命
  • 9.2 流程分析

    介绍了系统文件加密和文件解密的流程 那么我们本例主要涉及两个核心函数个函数Encrypt File和Decrypt File 使用Encrypt File函数完成文件加密功能 Decrypt File函数完成文件解密功能 下面介绍这两个函数
  • 跟着官网编写一个LLVMPass

    官网地址 https llvm org docs WritingAnLLVMPass html introduction what is a pass 一 创建文件 1 项目结构为 llvm project lib Transforms H
  • TscanCode C/C++静态分析开源分析工具安装与使用

    TscanCode是腾讯静态分析团队开发的一款开源免费的C C 静态分析工具 由于其比较简单实用 准确率较高 并且扫描C C 代码不需要进行编译 所以个人觉得对C C 项目开发挺有帮助的 就简单介绍一下该工具的安装与使用 1 Tscanco
  • 文件包含漏洞-日志注入

    文件目录 一 文件包含漏洞 1 文件包含概述 2 文件包含类型 二 文件包含 日志注入 1 日志注入概述 2 环境准备 3 配置环境 4 模拟网站环境 三 日志注入流程 一 文件包含漏洞 1 文件包含概述 文件包含漏洞是 Web 应用程序中
  • springboot的优化

    在SpringBoot的Web项目中 默认采用的是内置Tomcat 当然也可以配置支持内置的jetty 内置有什么好处呢 在SpringBoot的Web项目中 默认采用的是内置Tomcat 当然也可以配置支持内置的jetty 内置有什么好处
  • 互联网JAVA面试常问问题(三)

    一 volatile原理和使用场景 volatile 原理 volatile变量进行写操作时 JVM会向处理器发送一条Lock前缀的指令 将这个变量所在缓存行的数据写会到系统内存 Lock前缀指令实际上相当于一个内存屏障 也成内存栅栏 它确
  • LED用DMX512协议整个系统怎么连接?

    提问1 EIA485规范只支持 雏菊链 或每段上最多以32个 单元负载 所构成的串行网络 DMX512不是可以支持512个通道吗 那是不是说 超过32个的情况下需要使用中继 提问2 控制器 接收端1 接收端2 接收端n 电阻 GND 这样的
  • BIO、NIO、AIO理解

    一 到底什么是BIO NIO AIO 这些可以理解为是Java语言对操作系统的各种IO模型的封装 程序员在使用这些API的时候 不需要关系操作系统层面的知识 也不需要根据不同操作系统编写不同的代码 只需要使用Java的API就可以了 二 B