Java中有两套IO模型,分别是传统IO和JDK1.4新引入的NIO(叫New IO或 Non-Blocking IO,后者更能体现它的设计理念)。
在传统IO模型中,网络IO是基于数据流(Stream)的,数据的输入和输出分别对应InputStream和OutputStream。流数据只能逐字节的读取(或写入),直到全部数据读取(或写入)完毕,流数据只能单向的读取(或写入),无缓存区,没法对数据进行先后移动访问。
在NIO模型中,网络IO是基于Channel和IO多路复用技术,实现原理更接近OS的IO模型,网络读IO的两阶段模型(数据准备、数据拷贝)中,数据准备阶段是Non-Blocking的,Channel和Buffer配合主要处理的是数据拷贝阶段的工做。缓存
传统IO模式,在处理数据流过程当中,线程处于阻塞状态,直到流处理(读取后写入)完毕,因此一个线程只能处理一个IO任务,若是IO未准备就绪无数据(或不可写)线程只能一直等待,直到能够有数据(或可写);
NIO模式,采用IO多路复用技术,使用选择器(Selector)监控一组IO,虽然Selector线程也处于阻塞状态,但一个线程能够同时处理多个IO任务,当IO就绪时,Selector返回就绪的IO并由程序进行处理。服务器
如上图所示,NIO的核心涉及3部分,分别为Selector、Channel和Buffer。在描述数据读写操做时,须要特别注意读写的主体,从Channel的角度,读数据是指把数据从Channel读取数据写入Buffer,写数据是指把Buffer的数据写入Channel;从Buffer的角度,写数据是指把数据写入Buffer,读数据是指从Buffer中读取数据写入Channel或转换为其余数据类型。网络
Selector是NIO多理复用的执行组件,系统在处理多个IO任务时,能够把每一个IO任务以Channel的方式注册到Selector,经过Selector.select()统一监控向其注册的Channel的状态,select()方法会阻塞,直到其中有Channel的IO状态准备就绪有数据可读(或IO可写)时,select()返回就绪IO的Channel由程序处理相关事件。
Selector只能监听SelectableChannel类型的Channel,主要应用场景为网络IO等数据准备阶段时间较长的IO,SocketChannel属于此类型,而FileChannel不属于。app
Selector的主要方法:post
Channel是NIO中数据处理的入口,每一个Channel对应着一个IO的底层数据Buffer,负责对Buffer的写入或读取操做。根据IO类型不一样,Java中提供了以下通用Channel实现。this
SocketChannel的主要方法:spa
register():向Selector注册Channel感兴趣的事件,事件包括:线程
当Socket关闭后,会触发对端的OP_READ事件,但此时read()返回-1或throw IOexception,能够经过这两个判断来肯定对端是否关闭,并调用close()方法关闭SocketChannel并执行清理工做。
Buffer本质上是一块连续的内存区域,提供数据缓存功能和Channel对数据的读写操做能力。NIO提供了 ByteBuffer / CharBuffer / DoubleBuffer / FloatBuffer / IntBuffer / LongBuffer / ShortBuffer
Buffer类型,实现读Java基本类型的缓存支持。对Buffer的读写操做过程当中,涉及3个关键参数,即postion、limit和capacity。其中position和limit在读/写操做时具备不一样的意义,而capacity读写时都是表示Buffer的大小。Buffer初始处于写状态,数据写入后,经过flip切换的读取状态,读取完后,再经过clear清除数据,准备写入。设计
Capacity
Buffer是一个固定大小的内存缓存区,Capacity就表示其总长度。向Buffer写入的数据字节(byte/char/int/long等)总长度不能超过Capacity。当Buffer写满时,须要(读取)清空后才能继续写入。code
Position
Position初始位置为0,当Buffer读写模式切换时,也会自动重置为0。
读取模式时,Position表示当前读取数据的位置,读取当前位置数据后,Position自动移到下一个可读位置,读取位置不能超过Limit。
写入模式时,Position表达当前写入数据的位置,在当前位置写入数据后,Position自动移到下一个写入位置,写入位置不能超过Capacity - 1。
Limit
读模式时,Limit表示Buffer中可读取数据长度,当Buffer从写模式切换到读模式时,Limit值等于写模式时的Position。
写模式时,Limit表示可写入数据总长度,其值等于Capacity - 1。
Buffer的主要方法:
public final Buffer flip() { limit = position; position = 0; mark = -1; return this; } public final Buffer clear() { position = 0; limit = capacity; mark = -1; return this; } public final Buffer rewind() { position = 0; mark = -1; return this; }