我们知道io只是输入输出,在java语言中分为同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO,现在的IO,一般是用作两种用途:一种是进行文件或者目录的操作(将不同的输入输出源抽象成流,所以流本质就是从起源到接受的有序数据,即流就是数据),另一种是进行套接字(Socket)操作(分为ServerSocket——监听其他通讯实体连接请求,是服务器端和Socket——连接到制定的服务器,是客户端)。
BIO是传统的IO,又称为阻塞性IO
java中的BIO存在两类,一共四个基类,分别是OutputStream/InputStream(字符流),Writer/Reader(字节流),后者两类是为了方便字节操作对前者进 行了封装,底层还是字符流(具体他们的区别,我们在后续的博文中比较)。
在进行文件或者目录操作的时候,将数据的接收方抽象成水管,水管中的水滴就是最小的数据传输单元(对于字节流是一个字节,对于字符流是一个字 符),通过隐式的记录指针来表示当前准备读取水滴的位置,指针自动向后移动,数据连续不断发送,每个基类中也有对应的方法对指针进行操纵,但是不能操 作读取的方向,BIO数据的传输是单向的,也就是说,不能灵活的操作流中的数据发送的方向
在进行套接字操作的时候,accept()、connect()、write()、read()调用时会产生阻塞。一个请求一个线程,并发过高,线程压力过大,会导致宕机,所以不能应对高并发,高访问量的场景。
NIO(N可以理解为new,也可以理解为NonBlock)是新IO,非阻塞IO
NIO是面向通道(channel)的,面向缓冲区(buffer)的,通道相当于提供了运算环境,缓冲区是运输数据的载体。
在进行文件或者目录操作的时候,可以通过通道或者缓冲区进行数据的读写(同一个channel中既可以读也可以写,是双向的),所有对数据的操作都是通 过缓冲区操作的。
在进行套接字操作的时候,accept()、connect()、write()、read()调用时不会产生阻塞。一个有效请求对应给一个线程,连接没有数据处理就没有线程等待。所以能应对高并发,高访问量的场景。
AIO是异步IO,Netty在之前的版本中用过,但是现在不用了,现在这项技术还没有具体的行业版。
转自其他文章:
Java对BIO、NIO、AIO的支持:
-
Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
-
Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
-
Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,
BIO、NIO、AIO适用场景分析:
-
BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
-
NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
-
AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。
另外,I/O属于底层操作,需要操作系统支持,并发也需要操作系统的支持,所以性能方面不同操作系统差异会比较明显。
在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作。
在比较这两个模式之前,我们首先的搞明白几个概念,什么是阻塞和非阻塞,什么是同步和异步,同步和异步是针对应用程序和内核的交互而言的,同步指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪,而异步是指用户进程触发IO操作以后便开始做自己的事情,而当IO操作已经完成的时候会得到IO完成的通知。而阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式,说白了是一种读取或者写入操作函数的实现方式,阻塞方式下读取或者写入函数将一直等待,而非阻塞方式下,读取或者写入函数会立即返回一个状态值。
一般来说I/O模型可以分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞IO
同步阻塞IO:在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式!
同步非阻塞IO:在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。其中目前JAVA的NIO就属于同步非阻塞IO。
异步阻塞IO:此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性!
异步非阻塞IO:在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。目前Java中还没有支持此种IO模型。
注:
阻塞与非阻塞是指这个方法在执行的时候是否阻塞程序的继续执行其他与客户端和服务器端无关程序。
附:BIO阻塞产生与放开的条件
* accept()会产生阻塞
* Listens for a connection to be made to this socket and accepts
* it. The method blocks until a connection is made.
* 当一个客户端连入的时候,阻塞放开,如果没有客户端连入,就会产生阻塞
* read()会产生阻塞
* This method
* blocks until input data is available, the end of the stream is detected,
* or an exception is thrown.
* 当有数据可读时,阻塞放开。
* write()也会产生阻塞,一直往出写数据,但是没有任何一方读这个数据的时候
* 写到一定量就会产生阻塞。(是写到网卡的设备缓冲区里)
* connect()一会产生阻塞,
* Connects this socket to the server with a specified timeout value.
* A timeout of zero is interpreted as an infinite timeout. The connection
* will then block until established or an error occurs.
* 直到成功连入服务端或抛异常,阻塞放开