异步IO
异步IO是一种没有阻塞地读写数据的方法,通常,在代码进行read()或write()等操作的时候,代码会阻塞直至由可供读取的数据。
另一方面,异步IO调用不但不会阻塞,相反,可以注册对特定IO事件,诸如数据可读、新连接到来等。当发生这种事情的时候,系统会自动通知。
异步IO的另一个优势是它允许同时大量的输入和输出执行IO。同步程序常常要求助于轮询,或者创建许许多多的线程以处理大量的连接。使用异步IO,可以监听任何数量的通道上的事件,不用轮询,也不需要额外的线程。
Selector
Selector是Java NIO的核心对象之一,Selector是一个对象,它可以注册到多个Channel中,监听各个Channel上发生的事件,并且根据事件情况决定Channel读写。这样,通过一个线程管理多个Channel,就可以处理大量网络连接了。
采用Selector模式的好处
有了Selector,我们就可以利用一个线程来处理所有的channels,由于每个线程都会占用一定的系统资源,所以,对系统而言,使用的线程越少越好。
如何创建一个Selector
异步IO中的核心对象名为Selector。Selector就是您注册对各种IO事件有兴趣的地方,而且,当那些事件发生时,就是这个对象告诉您所发生的事件。
Selector selector = Selector.open();
然后,就需要注册Channel到Selector了
如何注册Channel到Selector
为了能让Channel和Selector配合使用,我们需要把Channel注册到Selector上,通过调用channel.register方法来实现注册。
channel.configureBlocking(fasle);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
注意,注册的Channel必须设置成异步模式才可以,否则异步IO就无法工作,这意味着我们不能把FileChannel注册到Selector,因为FileChannel没有异步模式,但是,网络编程中的SocketChannel是可以的。
需要注意的是register()方法的第二个参数,它是一个"interest set",意思是注册的Selector对Channel有哪些事件感兴趣,事件类型有四种:
- Connect
- Accept
- Read
- Write
通道触发了一个事件意思是该事件已经Ready(就绪)。所以,某个Channel成功连接到另一个服务器称为Connect Ready。一个ServerSocketChannel准备好接收新连接称为Accept Ready,一个有数据可读的通道可以说是Read Ready,等待写数据的通道可以说是Write Ready。
上面的四个事件对应到SelectionKey的四个常量:
SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE
关于SelectionKey
register()的调用的返回值是一个SelectionKey。SelectionKey代表这个通道在此Selector上的这个注册。当某个Selector通知您某个传入事件时,它是通过提供对应于该事件的selectionKey来进行的。SelectionKey还可以用于取消通道的注册。SelectionKey中包含以下属性:
- The interest set
- The ready set
- The Channel
- The Selector
- An attached object(optional)
Interest Set
就像我们在前面讲到的把Channel注册到Selector来监听感兴趣的事件, interest set就是你要选择的感兴趣的事件的集合。可以通过SelectionKey对象来读写interest set:
int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
Ready Set
ready set是通道已经准备就绪的操作的集合,在一次选Selection之后,你应该会首先访问这个ready set。Selection将在下一小节进行解释,可以这样访问ready集合
int readySet = selectionKey.readyOps();
可以用像检测interest集合那样的方法,来检测Channel中什么事件或操作已经就绪。但是,也可以使用以下四个方法,它们都会返回一个布尔类型: