BIO
这个其实就是最传统的网络通信模型,就是BIO,同步阻塞式IO,简单来说大家如果参加过几个月的培训班儿应该都知道这种BIO网络通信方式。就是服务端创建一个ServerSocket,然后客户端用一个Socket去连接那个ServerSocket,然后ServerSocket接收到一个Socket的连接请求就创建一个Socket和一个线程去跟那个Socket进行通信。
然后客户端和服务端的Socket,就进行同步阻塞式的通信,客户端Socket发送一个请求,服务端 Socket进行处理后返回响应,响应必须是等处理完后才会返回,在这之前啥事儿也干不了,这可不就是同步么。
这种方式最大的坑在于,每次一个客户端接入,都是要在服务端创建一个线程来服务这个客户端的,这会导致大量的客户端的时候,服务端的线程数量可能达到几千甚至几万,几十万,这会导致服务器端程序的负载过高,最后崩溃死掉。
要么你就是搞一个线程池,固定线程数量来处理请求,但是高并发请求的时候,还是可能会导致各种排队和延时,因为没那么多线程来处理。
NIO
JDK1.4中引入了NIO,这是一种同步非阻塞的IO,基于Reactor模型。
NIO中有一些概念:
比如Buffer,缓冲区的概念,一般都是将数据写入Buffer中,然后从Buffer中读取数据,有IntBuffer、LongBuffer、CharBuffer等很多种针对基础数据类型的Buffer。
还有Channel,NIO中都是通过Channel来进行数据读写的。
包括Selector,这是多路复用器,selector会不断轮询注册的channel,如果某个channel上发生了读写事件,selector就会将这些channel获取出来,我们通过SelectionKey获取有读写事件的channel,就可以进行IO操作。一个Selector就通过一个线程,就可以轮询成千上万的channel,这就意味着你的服务端可以接入成千上万的客户端。
这块其实相当于就是一个线程处理大量的客户端的请求,通过一个线程轮询大量的channel,每次就获取一批有事件的channel,然后对每个请求启动一个线程处理即可。
这里的核心就是非阻塞,就那个selector一个线程就可以不停轮询channel,所有客户端请求都不会阻塞,直接就会进来,大不了就是等待一下排着队而已。
这里的核心就是因为,一个客户端不是时时刻刻都要发送请求的,没必要死耗着一个线程不放吧,所以NIO的优化思想就是一个请求一个线程。只有某个客户端发送了一个请求的时候,才会启动一个线程来处理。
所以为啥是非阻塞呢?因为无论多少客户端都可以接入服务端,客户端接入并不会耗费一个线程,只会创建一个连接然后注册到selector上去罢了,一个selector线程不断的轮询所有的socket连接,发现有事件了就通知你,然后你就启动一个线程处理一个请求即可,但是这个处理的过程中,你还是要先读取数据,处理,再返回的,这是个同步的过程。
所以NIO是同步非阻塞的。
AIO
AIO是基于Proactor模型的,就是异步非阻塞模型。
每个连接发送过来的请求,都会绑定一个buffer,然后通知操作系统去异步完成读,此时你的程序是会去干别的事儿的,等操作系统完成数据读取之后,就会回调你的接口,给你操作系统异步读完的数据。
然后你对这个数据处理一下,接着将结果往回写。
写的时候也是给操作系统一个bufer,让操作系统自己获取数据去完成写操作,写完以后再回来通知你。
工作线程,读取数据的时候,是说,你提供给操作系统一个buffer,空的,然后你就可以干别的事儿了,你就把读数据的事儿,交给操作系统去干,操作系统内核,读数据将数据放入buffer中,完事儿了,来回调你的一个接口,告诉你说,ok,bufer交给你了,这个数据我给你读好了
写数据的时候也是一样的的,把放了数据的buffer交给操作系统的内核去处理,你就可以去干别的事儿了,操作系统完成了数据的写之后,就会来回调你,告诉你说,ok,哥儿们,你交给我的数据,我都给你写回到客户端去了
同步阻塞、同步非阻塞、异步非阻塞
但是这里为啥叫BIO是同步阻塞呢?这个其实不是针对网络编程模型来说的,是针对文件IO操作来说的,因为用BIO的流读写文件,是说你发起个IO请求直接hang死,必须等看搞完了这次IO才能返回。
NIO为啥是同步非阻塞?就是说通过NIO的FileChannel发起个文件IO操作,其实发起之后就返回了,你可以干别的事儿,这就是非阻塞,但是接下来你还得不断的去轮询操作系统,看IO操作完事儿了没有。
AIO为啥是异步非阻塞?就是说通过AIO发起个文件IO操作之后,你立马就返回可以干别