多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
本章转载自:[https://blog.csdn.net/u012767184/article/details/88762935](https://blog.csdn.net/u012767184/article/details/88762935) **** :-: **BIO/NIO/AIO三者比较** | 名称 | 说明 | 应用场景 | 补充 | 优点 | 缺点 | |--|--|--|--|--|--| | BIO | 阻塞IO | Apache Tomcat。<br/>适合用于并发量要求不高的场景。<br/><mark>服务器实现模式为一个连接一个线程</mark>。 | 连接数目比较小且固定的架构| 模型简单、编码简单 | 性能瓶颈低、不适合高并发 | |NIO |同步非阻塞IO | Nginx、Netty。<br/>适合用于要求高并发量的场景。<br/><mark>服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理</mark>。 | 连接数目多且连接比较短(轻操作)的架构 | 性能瓶颈高 | 模型复杂、编码复杂、需处理半包问题 | | AIO | 异步非阻塞IO| 还不是特别成熟,底层也基本是多线程模拟,所以应用场景不多,Netty曾经用了,但又放弃了。<br/><mark>服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了,再通知服务器应用去启动线程进行处理</mark>。| 连接数目多且连接比较长(重操作)的架构 | | | 假设有这么一个场景,有一排水壶在烧水。 AIO的做法是,每个水壶上装一个开关,当水开了以后会提醒对应的线程去处理。 NIO的做法是,叫一个线程不停的循环观察每一个水壶,根据每个水壶当前的状态去处理。 BIO的做法是,叫一个线程停留在一个水壶那,直到这个水壶烧开,才去处理下一个水壶。 <br/> 可以看出AIO是最聪明省力,NIO相对省力,叫一个人就能看所有的壶,BIO最愚蠢,劳动力低下。 <br/> **1. 进程中的IO调用步骤** 1. 进程向操作系统请求数据。 2. 操作系统把外部数据加载到内核的缓冲区中。 3. 操作系统把内核的缓冲区拷贝到进程的缓冲区。 4. 进程获得数据完成自己的功能。 当操作系统在把外部数据放到进程缓冲区的这段时间内,如果应用进程是挂起等待的,那么就是同步IO,反之,就是异步IO,也就是AIO 。 <br/> **2. NIO线程处理速度是否比BIO的快** 不是。改用NIO只是让同时处理请求服务的数量增大,但是单个请求处理的时间是一样的。 <br/> 使用`Buffer`可以提高IO效率的原因(这里与IO流里面的BufferedXXStream、BufferedReader、BufferedWriter提高性能的原理一样):IO的耗时主要花在数据传输的路上,普通的IO是一个字节一个字节地传输,而采用了`Buffer`,比如通过`Buffer`封装的方法(比如一次读一行,则以行为单位传输而不是一个字节一次进行传输)就可以实现一大块字节的传输。比如:IO就是送快递,普通IO是一个快递跑一趟,采用了`Buffer`的IO就是一车跑一趟。很明显,`Buffer`效率更高,花在传输路上的时间大大缩短。 <br/> **3. NIO与AIO的区别** AIO是发出IO请求后,委托操作系统去获取IO权限并进行IO操作,当操作系统处理完成后通知AIO过来拿取结果即可。 NIO则是发出IO请求后,由线程不断尝试获取IO权限,获取到后通知应用程序自己进行IO操作。 <br/> AIO只是帮助你从内核中将数据复制到用户空间中,并调用你传入的回调方法。 NIO是需要程序自己从内核中将数据复制到用户空间中,并需要程序自己调用相应的处理逻辑。 <br/> **4. NIO线程处理流程** ![](https://img.kancloud.cn/96/15/961596074ef7d6626b45318fd10c902f_941x485.jpg) 程序需要调用`Selector.select()`方法,阻塞获取就绪的`Channel`,然后从`Channel`中读取数据做响应处理,这样虽然只有一个线程但可以处理多个请求,程序只需要处理已经准备好的`Channel`即可。 <br/> **5. 系统并发量很大,容易出什么问题** 答:内存溢出,cpu处理不过来。 (1)32位系统1个线程对象默认最大需要320kb内存,64位系统默认需要1M内存,业务对象还需要内存,容易造成内存不够。 (2)过多的线程需要OS频繁切换,也大大影响性能。 (3)Java的new一个对象,调用操作系统的api,都存放在java的堆里。线程创建时有个栈空间,会消耗内存。