Java通信模型BIO, NIO, AIO理解

本文内容整理于以下资料:

[1] https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/BIO-NIO-AIO.md

[2] https://www.jianshu.com/p/e0fa13f399ce

[3] https://zhuanlan.zhihu.com/p/23488863

[4] https://blog.csdn.net/h2604396739/article/details/82534253

 

一、同步与异步 :

 

同步:调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为。

异步:调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用者就可以继续后续的操作。而,异步方法通常会在另外一个线程中,“真实”地执行着。整个过程,不会阻碍调用者的工作。

 

二、经典BIO模型 Blocking IO:

 

服务器在while(true) 循环中调用 accept() 方法等待接收客户端的连接的方式监听请求,请求一旦接收到一个连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作。

存在问题:此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执行完成。

 

三、请求-应答模型:

 

解决问题:BIO模型的多线程版本,解决不能同时处理多个客户端请求的问题。

处理方式:服务器在接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成之后,通过输出流返回应答给客户端,线程销毁。

存在问题:在 Java 虚拟机中,线程是宝贵的资源,线程的创建和销毁成本很高,除此之外,线程的切换成本也是很高的。

 

四、伪异步IO:

 

  解决问题:伪异步I/O通信框架采用了线程池实现,因此避免了为每个请求都创建一个独立线程造成的线程资源耗尽问题。

  处理方式:后端通过一个线程池来处理多个客户端的请求接入,当有新的客户端接入时,将客户端的 Socket 封装成一个Task(该任务实现java.lang.Runnable接口)投递到后端的线程池中进行处理,JDK的线程池维护一个消息队列和 N 个活跃线程,对消息队列中的任务进行处理。即节省了大量线程创建和销毁的成本。

  存在问题:对于一个线程当进行read或write操作时该线程被阻塞,在此期间无法做其他事情,即仍然是同步阻塞的BIO模型,因此无法从根本上解决问题。

 

 五、NIO Non-Blocking IO:

 

  解决问题:NIO由原来的阻塞读写(占用线程)变成了单线程轮询事件,实现了非阻塞读与非阻塞写。

  对比:

  • 传统的BIO里面socket.read(),如果TCP RecvBuffer里没有数据,函数会一直阻塞,直到收到数据,返回读到的数据。
  • 而在NIO中,如果TCP RecvBuffer有数据,就把数据从网卡读到内存,并且返回给用户;反之则直接返回0,永远不会阻塞。

  处理方式:

  • NIO面向buffer(缓冲区),任何时候访问NIO中的数据,都是通过缓冲区进行操作。读数据时,是读到缓冲区中的; 写数据时,是写入到缓冲区中。
  • NIO通过Channel(通道) 进行读写。通道是双向的,可读也可写,而流的读写是单向的。无论读写,通道只能和Buffer交互。       
  • NIO的Selector(选择器)用于使用单个线程处理多个通道。由于线程之间的切换对于操作系统来说是昂贵的,因此Selector可以节约资源。

  存在问题:

  • 使用NIO != 高性能,当连接数<1000,并发程度不高或者局域网环境下NIO并没有显著的性能优势。
  • NIO并没有完全屏蔽平台差异,它仍然是基于各个操作系统的I/O系统实现的,差异仍然存在。使用NIO做网络编程构建事件驱动模型并不容易,陷阱重重。

NIO的特点:

  • 事件驱动模型
  • 避免多线程
  • 单线程处理多任务
  • 非阻塞I/O,I/O读写不再阻塞,而是返回0
  • 基于block的传输,通常比基于流的传输更高效
  • 更高级的IO函数,zero-copy
  • IO多路复用大大提高了Java网络应用的可伸缩性和实用性
NIO是同步非阻塞模型:
"阻塞与非阻塞指的的是当不能进行读写(网卡满时的写/网卡空的时候的读)的时候,I/O 操作立即返回还是阻塞;同步异步指的是,当数据已经ready的时候,读写操作是同步读还是异步读,阶段不同而已。“
"同步异步是指消息怎么返回,阻塞非阻塞是指拿消息的人状态。"

来源:Java NIO浅析, 美团技术团队及文章评论

六、AIO Asynchronous I/O

AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 的应用还不是很广泛,Netty 之前也尝试使用过 AIO,因此暂不进一步了解。

 

 

总结:

IO模型的发展过程:从最原始的BIO模型起始,引入了线程模型以同时响应多个客户端请求(请求-应答模型),引入线程池解决请求应答模型中的线程资源耗尽问题(伪异步IO),引入事件轮询机制解决IO阻塞问题(NIO)。

 

BIO是一个连接一个线程。
BIO的大并发问题:在使用同步I/O的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时和多个服务器进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来单独处理。这样做虽然可以达到我们的要求,但同时又会带来另外一个问题。由于每创建一个线程,就要为这个线程分配一定的内存空间(也叫工作存储器),而且操作系统本身也对线程的总数有一定的限制
 
NIO是一个请求一个线程(非有效请求即不间断查询是否读完)
NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。


AIO是一个有效请求一个线程。
当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。  即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。

posted @ 2020-03-23 16:21  Han-helloWorld  阅读(219)  评论(0)    收藏  举报