比对项 | 传统IO | NIO(Non-Block IO) |
---|---|---|
缓冲 | 面向流 | 面向块(缓冲) |
IO模型 | 阻塞IO | 非阻塞IO |
线程复用 | 无 | 选择器 |
传统JavaIO是面向流的I/O。流I/O一次处理一个字节。NIO则是面向块的I/O,每次操作都是以数据块为单位。它们的差距就好象两个人吃饭,一个人一粒一粒的吃,另一个人狼吞虎咽,快慢显而易见。NIO中引入了缓冲区(Buffer)的概念,缓冲区作为传输数据的基本单位:块。Buffer缓冲区的引入,是NIO与传统IO的一个重要区别.在传统IO中,都是针对流数据的操作,在NIO中是针对缓冲区数据操作。 缓冲区的实质是一个数组,最常用的是ByteBuffer.事实上每一种java基本类型都对应一种缓冲区类型.
Channel是数据通道,它与传统IO Stream的主要区别在于通道是双向读写的,流只是朝着一个方向移动 Channel可以主要分为2大类:一类为网络数据读写的SelectableChannel和文件读写的FileChannel.
Selector类是NIO的核心类,Selector能够检测多个注册的通道上,是否有事件发生,如果有事件发生,便获取事件然后针对每个事件进行相应的响应处理。这样一来,只是用一个单线程就可以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护多个线程,并且避免了多线程之间的上下文切换导致的开销。
JDK4提供了非阻塞IO的能力,JDK7开始提供异步IO能力(NIO2.0)
在数据没有读写完成之前,线程不可以进行下一步操作,这样线程只好眼睁睁的在那里傻等。 下图为采用阻塞IO的通讯模型的服务端,由一个Acceptor监听客户端的链接,它接收到客户端的请求后创建一个新的线程进行链路处理,通过输出流应答客户端,线程销毁。简单地说就是,一客户端请求对应一个线程.
这个模型有一个很大的问题,就是当客户端请求规模较大的时候,一客户端请求创建一个线程,导致服务器资源消耗殆尽,造成宕机。
为了解决这个问题,在服务端使用线程池。线程的数量是固定的(线程池),使用的时候借用获取,用完之后不是销毁而是将线程归还给线程池。但这个解决方案,在并发量超过线程池内现成的数量时,会导致获取线程的时间拉长,应答缓慢,延迟较大!这个方案,我们称为"伪异步IO"!
在数据没有读写完成之前,调用线程可以离开也不用轮询。在数据准备好之后,多路复用监听会主动通知调用线程。这通常需要复杂的设计,为降低NIO与AIO的实现复杂度,我们可以使用Netty或者mina等框架。
喜欢 (9)or分享 (0)