IO,BIO,NIO理解
一、前提基础知识
1.1.同步与异步
关注的是被调用方的执行方式及返回时机。
同步:被调用方做完内部所有事情后再返回,同步调用的调用者一定会得到被调用方的结果。
异步:被调用方先返回,再做事情,做完所有事情后再通知调用方(回调方法),异步调用方不会立即得到被调用方的结果,而是当被调用方执行完成后通过回调函数处理该结果。
1.2.阻塞与非阻塞
关注的是调用方在被调用方返回结果之前的这段时间,是否处于等待状态。
阻塞:调用方在等待被调用方返回结果的这段时间什么都不干;
非阻塞:调用方在等待被调用方返回结果的这段时间去处理其他事情。
1.3.同步/异步与阻塞/非阻塞对比
同步/异步从行为角度描述事物,针对的对象是被调用方,主要关注被调用方的执行方式及返回时机。
阻塞/非阻塞描述当前事物的状态(等待调用结果时的状态),针对的对象是调用方,主要关注调用方是否在等待被调用方返回结果的这段时间是否处于等待状态。
1.4.并发与并行
并发:一个时间段内,几个程序都在同一个CPU上运行,但任意一个时刻点上只有一个程序在处理机上运行。
并行:一个时间段内,几个程序在不同的CPU上运行,任意一个时刻点上,有多个程序在同时运行,并且多道程序之间互不干扰。
二、I/O
I/O是Input输入/Output输出的简称,通常指数据在内部存储器(内存)
和外部存储器(硬盘、优盘)
或其他周边设备之间的输入和输出。
输入/输出是信息处理系统(计算机)与外部世界(人类或另一信息处理系统)之间的通信。
三、BIO(Blocking I/O)
同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
3.1.BIO通信模型:一请求一应答
采用BIO通信模型的服务端,一般由一个独立的Acceptor线程负责监听客户端的连接,一般通过while(true)循环中服务端会调用accept()方法等待接收客户端的连接的方式监听请求;一旦接收到一个客户端的连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作,此时当前线程不能再接收其他客户端连接请求,只能等待当前客户端的操作执行完成(可通过多线程来支持多个客户端的连接)
一请求一应答通信模型:服务端接收到客户端连接请求后为每个客户端创建一个新的线程进行链路处理,处理完成后,通过输出流返回应答给客户端,线程销毁。
客户端并发访问量增加后一请求一应答模型的弊端
:若连接不做任何事情就会造成不必要的线程开销,在Java虚拟机中,线程是宝贵的资源,线程的创建、销毁、切换成本很高,如果并发量增加会导致线程数急剧膨胀可能会导致线程堆栈溢出、创建新线程失败等问题,最终导致服务端不能对外提供服务。
3.2.伪异步IO
伪异步I/O通信框架:用以解决一请求一应答模型的弊端,后端通过一个线程池来处理多个客户端的请求接入,形成客户端个数M:线程池最大线程数N的比例关系,M可以远大于N,通过线程池就可以灵活的调配线程资源,避免每个请求都创建一个独立线程造成的线程资源耗尽,并设置线程池最大值,防止由于海量并发接入导致线程耗尽。
当活动连接数不是特别高(小于1000),BIO模型比较实用,每个连接专注于自己的I/O且编程模型简单,线程池本身是个天然的漏斗,可以缓冲一些系统处理不了的连接或请求,当若面对上百万级连接时,BIO无能为力。
四、NIO(New I/O,Non-Block I/O)
同步非阻塞的I/O模型,支持面向缓冲的,基于通道的I/O操作方法,提供SocketChannel和ServerSocketChannel并支持阻塞(与BIO一样)与非阻塞两种模式。对于低负载、低并发的应用程序可以使用同步阻塞I/O提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用使用NIO的非阻塞模式。
4.1.NIO与IO区别/NIO特性/NIO核心组件
4.1.1.IO流是阻塞的,NIO的非阻塞的
IO:IO各种流是阻塞的,当线程调用read()或write()时,该线程被阻塞,直到一些数据被读取或数据完全写入,该线程在此期间不能再干任何事情了。
NIO:NIO可以进行非阻塞IO操作,eg:单线程从通道读取数据到buffer,同时继续做别的事情,当数据读取到buffer后,线程再继续处理数据。写数据也是一样的,一个线程请求写入一些数据到某通道,不需要等待它完全写入,这个线程同时可以去做别的事情。
4.1.2.IO是面向流的,NIO是面向缓冲区的
IO:面向流的IO可以将数据直接写入或将数据直接读到Stream对象中。
NIO:NIO直接读到Buffer中进行操作,所有数据都是用缓冲区处理的,读取数据是直接读到缓冲区,写入数据也是直接写入到缓冲区,任何时候访问NIO中的数据,都是通过缓冲区进行操作。
4.1.3.NIO通过Channel(通道)进行读写
Channel通道是双向的,可读也可写,而流是单向的。无论读写,通道只能与Buffer交互,通道可以异步的读写。
4.1.4.NIO有Selector选择器
Selector选择器用于使用单个线程处理多个通道,只需要较少的线程来处理这些通道,线程之间切换对于操作系统来讲是昂贵的,因此选择器会提高系统效率。
Thread->Selector->Channel1/Channel2/Channel3
4.2.NIO读写数据方式
NIO中所有的IO都是从Channel(通道)就开始的
从通道进行数据读取:创建一个缓冲区,然后请求通道读取数据->(Channel->Buffer)
从通道进行数据写入:创建一个缓冲区,填充数据,并要求通道写入数据->(Buffer->Channel)