NIO解决了两个阻塞问题:(1)IO本身的阻塞 (2)sockect.的accept阻塞;解决方案分别是新IO里面的channel/bytebuffer和selector.
问题1:
首先传统的IO是基于流的,是阻塞的,因为假设在网络应用中,如果数据没有到位(没有数据,或者不够:例如一个字节只传了第一位),而又不是流的结束(连接中断),那么这个时候read会阻塞至数据够了或者流结束或者有异常。
因此换句话说:必须读到数据,要不只能等到异常或者socket关闭返回-1,没有返回0的情况;
而新IO,可以读不到数据,可以返回0,只有等异常或者socke关系返回-1。因此不用阻塞线程。
换句话说:在socket不关闭情况下,传统IO必须读到数据,否则阻塞,而新IO可以不读到数据返回0从而不阻塞。
那么假设我们用装饰类bufferinputstream之类,是否解决了这个问题呢?其实并没有,因为理由有2个:
(1)装饰肯定以原来的inputstream为基础,既然基础的是阻塞的,那么必然也是阻塞的;
(2)例如BufferedReader.readline(),只是等于数据满足一行(遇到换行符)时,才会读出,减少不断读的代价,反而增大了要求;
从上面可以看出,传统的IO是阻塞的,那么只能借助于NIO来解决这个问题:
NIO用通道(这样仅仅是数据而已了)取代了流(字节流或者字符流),最大的特性是无阻塞的,即通道里面有多少数据就读写多少,立即返回,而不是阻塞。
(1)即使数据没有全部到位,也可以读写。
问题2:
select()是阻塞的,监听信道注册的感兴趣IO集发生事情(通道有读的数据,特别注意监听写的时候,是随时可以写的,取决于自己);
channel可以注册多个感兴趣IO ,对应不同的KEY,
当执行SELECT后,在执行selectkeys()就会把发生的IO事情选取出来做掉,记住做完之后要REMOVE,否则下次发生新事件,是在老的事情基础上增加的
因为从这个角度看,
NIO核心是SELECT,屏蔽了一个线程一个连接的缺点:假设都是长连接且用户过大,导致线程过多;
同时READ/WRITE是无阻塞的,配合SELECT就解决了死等现象;
遗留一个问题:假设NIO选择READ后,我读是否直接用等于0标记读数据结束了??
不知道自己理解的对不对?