NIO主要包括了三大核心模块:Channel、Buffer以及Selector
传统 IO 基于字节流和字符流进行操作,而 NIO 基于 Channel 和 Buffer(缓冲区)进行操作的,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。Selector(选择区)用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个线程可以监听多个数据通道。如下图所示
1.selector通过单线程的机制监听多个Channel的事件,在正常的开发环境下,并不是只用一个线程来监听,通常会创建少量的线程来监听事件的响应。
2.每个channel都对应着响应的缓冲区,可以双向的读写操作。
3.每个channel都会注册在一个selector上,当事件(建立连接、读写操作)发生时,就能及时监听到
4.Buffer实际上是一个内存块,底层实际上是一个数组
核心模块介绍
1.Channel
Channel就是一个通道,和我们的Stream(流)类似,但是channel既可以进行读,也可以进行写。
Channel是一个接口
public interface Channel extends Closeable{}
其主要实现类为
1. FileChannel 用于文件的数据读写
2. DatagramChannel 用于 UDP 的数据读写
3. SocketChannel 用于 TCP 的数据读写
4. ServerSocketChannel 用于 TCP 的数据读写
2.Buffer
Buffer是一个缓冲区,实际上是一个容器,是一个连续的数组结构。
在 NIO 中,Buffer 是一个顶层父类,它是一个抽象类,常用的 Buffer 的子类有:ByteBuffer、IntBuffer、 CharBuffer、 LongBuffer、 DoubleBuffer、FloatBuffer、ShortBuff,其中,最常用的是ByteBuffer,将文件或者对象转化成byte类型的数组,然后通过channel和byteBuffer进行传输。
3.Selector
selector是NIO的核心类,通过它来检测各个通道上是否有事件发生,并针对事件进行相应的处理。通过少量的线程就可管理多个socket连接,在很大程度上减少了线程创建和维护带来的浪费。同时,selector是在内核态不断轮询监听通道上的事件,因此,还能够避免多线程上下文切换带来的开销。
相关方法:
public static Selector open();//得到一个选择器对象
public int select(long timeout);//监控所有注册的通道,当其中有 IO 操作可以进行时,将
对应的 SelectionKey 加入到内部集合中并返回,参数用来设置超时时间
public Set<SelectionKey> selectedKeys();//从内部集合中得到所有的 SelectionKey