1.阻塞和非阻塞java
阻塞和非阻塞是进程在访问数据的时候,进程需不须要等待。linux
1. 阻塞api
当数据没有准备好时,都会一直等待缓冲区中的数据准备就绪以后才会开始处理,不然会一直等待下去。数组
2. 非阻塞安全
当进程访问到数据缓冲区时,若是数据还未准备好,会直接返回不会一直等待,若是数据已经准备好了,也会直接返回。服务器
2.同步和异步网络
同步和异步都是基于应用程序和操做系统处理IO事件锁采用的方式。并发
同步:框架
同步是应用系统直接参与IO读写操做。在处理IO事件的时候必须阻塞在某个方法上面等待IO事件完成(阻塞IO事件或则经过轮询IO事件的方式)。异步
异步:
3.Java IO模型,NIO,BIO,AIO
NIO:
jdk1.4 linux多路复用技术 (select模式)实现IO事件的轮询方式同步非阻塞的模式,这种方式目前是主流的网络通讯模式。
目前市面上的这种模式的框架有:Mina, netty,mina2.0,nett5.0--网络通讯框架比直接写
NIO要容易些,而且代码可读性更好。
BIO:
JDK1.4之前使用都是BIO阻塞IO,主要是阻塞到线程来操做的,但对于线程的开销原本就是性能的浪费。
AIO:
jdk1.7(NIO2)才是实现真正的异步aio,学习 它的思想主要是借鉴了linux epoll模式。
4.NIO的原理
先来看一张图
网络通讯中,NIO也提供了SocketChannel和ServerSocketChannel两种不一样的套接字通道来实现,这两个类都实现了Channel接口。它们能够设置阻塞与非阻塞两种模式,为了实现高负载高并发都采起非阻塞的模式。
NIO采用缓冲区Buffer,实现对数据的读写操做,缓冲区是固定大小,并由内部状态记录有多少数据被放入或者取出。
与阻塞IO不一样,阻塞IO采用阻塞式流(Stream)的方式进行读写,流是单向的只能向一个方向读数据或者写数据。
而通道是双向的,能够同时在通道上发送和读取数据,并且是非阻塞的,在没有数据可读可写时能够去作别的事情。
主要使用了ServerSocketChannel,SocketChannel,Selector,ByteBuffer这么几个类。
1.ServerSocketChannel(服务端使用类)
采用api文档解释:经过调用此类的 open 方法建立服务器套接字通道。新建立的服务器套接字通道已打开,但还没有绑定。
试图调用未绑定的服务器套接字通道的 accept 方法会致使抛出 NotYetBoundException。
可经过调用相关服务器套接字的某个 bind 方法来绑定服务器套接字通道。多个并发线程可安全地使用服务器套接字通道。
2.SocketChannel(客户端使用类)
采用api文档解释:经过调用此类的某个 open 方法建立套接字通道。
新建立的套接字通道已打开,但还没有链接。试图在未链接的通道上调用 I/O 操做将致使抛出 NotYetConnectedException。
可经过调用套接字通道的 connect 方法链接该通道;一旦链接后,关闭套接字通道以前它会一直保持已链接状态。
可经过调用套接字通道的 isConnected 方法来肯定套接字通道是否已链接。
3. Selector(选择器):
是 SelectableChannle 对象的多路复用器,Selector 能够同时监控多个 SelectableChannel 的 IO 情况,也就是说,利用 Selector可以使一个单独的线程管理多个 Channel,selector 是非阻塞 IO 的核心。
当通道使用register(Selector sel, int ops)方法将通道注册选择器时,选择器对通道事件进行监听,经过第二个参数指定监听的事件类型。
其中可监听的事件类型包括如下:
读 : SelectionKey.OP_READ (1)
写 : SelectionKey.OP_WRITE (4)
链接 : SelectionKey.OP_CONNECT (8)
接收 : SelectionKey.OP_ACCEPT (16)
若是须要监听多个事件是:
int key = SelectionKey.OP_READ | SelectionKey.OP_WRITE ; //表示同时监听读写操做。
4.Bytebuffer
ByteBuffer类是Buffer的子类,Buffer是顶层抽象类,ByteBuffer继承Buffer,也是抽象类。看看继承结构。
ByteBuffer是在javaNIO中常使用的一个缓冲区类,使用ByteBuffer能够进行高效的IO操做。经过 ByteBuffer提供不少读写的方法put(),get(),而且还包含四个很重要的属性。
容量(capacity)
capacity指的是缓冲区可以容纳元素的最大数量,这个值在缓冲区建立时被设定,并且不可以改变,以下,咱们建立了一个最大容量为10的字节缓冲区;
ByteBuffer bf = ByteBuffer.allocate(10);
上界(limit)
limit指的是缓冲区中第一个不能读写的元素的数组下标索引,也能够认为是缓冲区中实际元素的数量;
位置(position)
position指的是下一个要被读写的元素的数组下标索引,该值会随get()和put()的调用自动更新;
标记(mark)
一个备忘位置,调用mark()方法的话,mark值将存储当前position的值,等下次调用reset()方法时,会设定position的值为以前的标记值;
四个属性值之间的关系
根据以上四个属性的定义,咱们能够总结出它们之间的关系以下:
0 <= mark <= position <= limit <= capacity
5.NIO操做流程
服务端对象:ServerSocketChannel
客户端对象:SocketChannel
选择器:Selector selector= Selector.open();//这样就打开了选择器
得到选择器中的事件集合:Set<SelectionKey> key= selector.selectedKeys()
key. isAccptable:是否能够接受客户端的链接
key, isConnctionable:是否能够链接服务端
key, isReadableo:缓冲区是否可读
key, isWriteableo:缓冲区是否可写
6.如何注册
channel.regist(selector, Selectionkey.OP_WRITE)//注册写事件
channel.regist(Selector, Selectionkey.OP_READ);//注册读事件
channel.regist(Selector, Selectionkey.OP_CONNECT);//注册链接事件
channel.regist(Selector, Selectionkey.OP_ACCEPT);//注册请求事件
以上注册成功以后就能够经过SelectionKey来判断io事件是否就绪,而后能够进行后续操做。
来源连接 :