NIO浅析
1、为什么使用NIO?
一句话:都是阻塞惹的祸
BIO服务端存在请求阻塞阻塞,不能同时处理多个客户端的连接请求。
有人提出在服务端accept后,创建线程去处理请求。这种方式有不足:互联网高并发场景下,过多的线程会导致OS的频繁切换,大大影响性能,即使使用线程池创建线程。
以上用线程池会产生请求时间长,拒绝服务,但是服务器资源可能并未高效率使用。
2、使用NIO的三个点
A、Selector
为什么使用selector
因为比较老的办法是,server的一个线程需要不断while(true)去轮询来 发现是否有新连接或者是否有数据读写操作。在高并发情况下,性能是很低的
选择器,一个选择器可以检测多个通道channel
具体使用:
selectKeys: selector监听到就绪的channel的keys。
先获取到所有keys,再对key进行循环判断。对key(是否连接判断、是否可读判断、是否写判断、数据是否已经发送)进行判断, 对不同情况进行处理。
B、channel
数据来源及去向,类比socket
首先创建ServerSocketChannel , 绑定端口号,随后注册到Selector,让Selector检测连接进来,指定使用非阻塞方式。
一旦进入key.isAccept()后,创建socketChannel通道,并注册到selector上监听数据是否到达。
一旦进入isReadable()后,交给一个线程池的一个任务去异步处理。
一定要进行cancel()操作,原因:因为是交给线程池异步处理,所以存在异步未处理,key又被selector监听一次,再次交给线程处理,会产生重复select,从而再次产生数据。虽然我们对key进行以下处理:
Selector会产生2个集合(所有注册通道集合,就绪的key集合),上述代码只是移除就绪的key集合。
但是register集合中未被移除,还是存在被Select的可能,所以一定到取消注册(key.cancel())。
C、Buffer
sc.read(),写到buffer中。
1、形象比喻BIO&&NIO
2、多路复用解释