本章转载自:[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的堆里。线程创建时有个栈空间,会消耗内存。
- 网络通信
- 网络协议
- 端口和套接字
- TCP网络程序
- UDP网络程序
- 多线程聊天室
- 多线程
- 线程相关概念
- 线程实现方式
- 中断线程
- 线程生命周期
- 线程优先级
- 优先级规则
- 案例演示
- 线程同步机制
- 线程同步机制
- synchronized关键字
- ReentrantLock类
- Condition类
- 监视器概念
- volatile关键字
- final变量
- 死锁
- 线程局部变量
- 读/写锁
- 原子类
- 阻塞队列
- 工作规则
- 案例演示
- 常用阻塞队列
- 线程安全集合
- 高效的映射/集/队列
- 并发集视图
- 写数组的拷贝
- Arrays类的并行数组算法
- 同步包装器
- Callable与Future
- 执行器
- 线程池
- 预定执行
- 控制任务组
- Fork-Join框架
- 同步器
- 同步器
- 信号量
- CountDownLatch类
- CyclicBarrier类
- Exchanger类
- SynchronousQueue类
- 线程与Swing
- Swing与线程问题
- 两个原则
- Swing工作线程
- 单一线程规则
- 文件IO
- File类
- 文件输入输出
- ZIP压缩文件
- 集合
- 集合框架
- 集合接口
- 集合实现类
- 线程安全集合
- 集合算法
- 迭代器
- 集合排序
- JDBC
- JDBC是什么
- JDBC-ODBC桥
- JDBC驱动程序类型
- JDBC常用类与接口
- 数据库操作
- 连接数据库
- 增/删/改/查/预处理
- 事务
- 批处理
- commons-dbutils工具
- 安全问题
- Jedis
- 使用Jedis操作Redis数据库
- JSON转换
- 使用连接池
- 案例
- 单例破坏
- 单例定义
- 单例实现方式
- 懒汉式实现单例
- 饿汉式实现单例
- 单例破坏
- 类的单例破坏
- 枚举的单例破坏
- 克隆
- 克隆是什么
- 浅克隆
- 深克隆
- 注解
- 注解是什么
- 三大注解
- 内置注解
- 元注解
- 自定义注解
- NIO
- 相关概念
- BIO/NIO/AIO
- 多线程编程
- 线程同步
- 线程通信
- NIO
- NIO三大核心组件
- NIO网络编程
- NIO文件读写
- AIO
- Java8新特性
- Lambda表达式
- 方法引用
- 函数式接口
- 默认方法
- 什么是默认方法
- 默认方法语法格式
- 多个同名的默认方法问题
- 静态默认方法
- 默认方法实例
- Stream
- Stream是什么
- Stream示例
- Optional容器
- 新的日期时间API
- Base64
- SPI
- SPI是什么
- SPI与API的区别
- 常见场景
- 使用SPI需遵循的约定
- SPI使用步骤