理解同步、异步、阻塞、非阻塞
什么是同步,什么是异步?
同步:从时间上强调处理事情的结果,强调结果意味着对结果的迫不及待,不管结果如何,反正你要立即给我一个结果响应,一直处于等待状态。
异步:调用者发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时调用者在等待结果过程中浪费时间是极其难受的,这个时候我们可以处理其他的请求,被调用者通常依靠事件、回调等机制来通知调用者其返回结果。
什么是阻塞,什么是非阻塞?
阻塞:调用者发起一个请求,会一直等待请求结果返回,也就是当前线程会被挂起,没法做其他事情。
非阻塞:调用者发起一个请求,不用一直等待请求结果返回,可以去做其他的事情。
那么组合起来怎么理解呢?下面就用个简单的例子来说明下啊。
笔者是个喜欢吃麻辣烫的人,时不时偶尔要去楼下的麻辣烫店买点吃,主要他家的麻辣烫挺好吃的,也经常看到有蛮多人在那买。=^=
同步阻塞:我到麻辣烫店点好了东西,然后一直在那等着做好,偶尔还要问下:我的弄好了没。 同步非阻塞:我点好东西后,买水果或者买凉菜去了,过了会回来问下:我的弄好了没。异步阻塞:我是在买水果还是在买凉菜的时候,人家老板打电话过来了,说你的麻辣烫已经做好了,你过来拿就好了。 异步非阻塞:老板打电话不仅没让我过去拿,还说亲自给我送过来,让我安心的先买水果。(当然这个待遇是幻想出来的。。。)
先明白这几个概念,下面的定义会好理解多。
定义
BIO:同步阻塞I/O。一个连接一个线程,线程发起IO请求,不管内核是否准备好IO操作,从发起请求起,线程一直阻塞,直到操作完成。
NIO:同步非阻塞I/O。一个请求一个线程。服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。用户进程也需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问。
AIO:异步非阻塞I/O(注:JDK1.7升级了NIO类库,升级后的NIO类库被称为NIO2.0也就是AIO)。一个有效请求一个线程。用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。
BIO,AIO,NIO理解
1)BIO
BIO通信(一请求一应答)模型图如下(画的有点简陋):
BIO模型图
BIO通信模型的服务端,通常由一个独立的 Acceptor 线程负责监听客户端的连接。过程就是服务端接收到客户端的连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,然后销毁线程。
所以试想下这里有很大的问题,如果一个连接创建好了但是什么事都不做,会造成不必要的开销的。我们都知道,jvm中线程的创建和销毁成本很高。在并发量很大的情况下会导致创建的线程数急剧增长,可能会导致线程堆栈溢出、创建新线程失败等问题,服务就挂了。
2)NIO
NIO通信模型图:
NIO通信模型图
这里需要了解3个概念:缓冲区、通道、多路复用器。
缓冲区:即Buffer,Buffer是一个对象,它包含一些要写入或者要读出的数据,在NIO库中,所有数据读写操作都是用缓冲区处理的。具体一些实现就不说了,通常它是一个字节数组(ByteBuffer)。
通道:即Channel,通过它读取和写入数据,就像电话线一样。这里大家是不是有想到流(Stream),区别在于Stream是单向,而Channel是双向的。主要实现呢有以下:FileChannel、DatagramChannel、SocketChannel,通过英文名是不是可以看出个大概意思:文件IO、UDP和TCP。
多路复用器:即Selector,说白了是用来管理Channel的,Channel注册在Selector中,如果某个Channel上面有TCP连接接入,会被Selector轮询出来,通过相关处理进行后续的I/O操作。
服务端通信步骤:
1.创建ServerSocketChannel实例、并且绑定端口;
2.创建selector实例;
3.将ServerSocketChannel实例注册到选择器上,并监听accept事件;
4.有请求(accept)进来获取客户端的socketChannel的连接,将连接实例注册到选择器上;
5.IO操作(进入缓冲区Buffer);
6.关闭ServerSocketChannel实例
客户端通信步骤:
1.创建SocketChannel实例和Selector选择器;
2.连接服务器;
3.将SocketChannel注册到selector选择器;
4.发送数据;
5.关闭SocketChannel;
2)AIO
真正实现了异步IO, 基于windows上IOCP和Linux系统上Epoll 机制实现。底层实现是由操作系统完成的,与NIO不同,当进行读写操作时,只需直接调用AIO.read()或AIO.write()方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。
适用场景
BIO:适用于连接数目比较小,并且一次发送大量数据的场景;
NIO:适用于连接数目多,连接比较短,常用于聊天服务器开发工作(榜上有名的Netty是很大的实践);
AIO:适用于连接数目多,连接比较长。这个目前市面上应用还不是很广泛。
总结
以上只是讲解了BIO,NIO,AIO基础东西,没有写示例,大家可以网上查询资料,看文档的方式尝试写个demo出来,对理解更有用处。I/O范围很宽的,根据模式又分为Reactor模式和Proactor模式。Reactor模式又分为单线程Reactor模式、多线程Reactor模式、主从多线程Reactor模式。所以要学习的路还很长。我太难了!