Java NIO 使用selector进行IO复用

JAVA ginotang 1140℃ 0评论

IO复用的目的

在常规的IO操作过程中,例如文件的读写操作等,都是阻塞式的。当然,这些问题可以通过线程来解决;然而创建线程的成本是比较高的,而且可能会造成一系列的问题,例如线程之间的切换和数据共享,死锁等。虽然现代cpu的计算速度日新月异,但是,如果有一种方案可以不使用线程,而且又可以进行非阻塞式IO操作,那可乐而不为呢。selector就是这样一个解决方案。

Selector的作用

简单来说,Selector可以对多个已注册的SelectableChannel的状态进行监控,例如某一个Channel是否可写、可读。一旦这些Channel的状态发生变化,我们就可以在程序中针对特定的状态进行操作。

SelectionKey

SelectionKey的作用是对SelectableChannel的状态进行维护和反馈,channel对象的状态有以下四种:

  • readable        通道是否可读
  • writable        通道是否可写
  • connectable        通道是否可连接
  • acceptable        通道是否可被接受

另外,SelectionKey里面还维护了一个和此对象绑定的channel对象,通过SelectionKey的channel()方法可以获得。

Interest Set

Intereset set是指我们感兴趣的操作集合,这个集合和上面的SelectionKey对象的状态互相对应,它们分别是:

  • OP_READ
  • OP_WRITE
  • OP_CONNECT
  • OP_ACCEPT

调用SelectableChannel的register方法的时候,就可以指定这些集合。例如,如果想知道一个通道是否准备好读取数据,就可以这样做:

第一行代码是必须的,channel的blocking模式必须设置为false才能被selector使用。一次还可以设置多个集合:

Ready Operations

Ready Operations指那些已经就绪的通道状态,通过检测SelectionKey的Interest Set,就可以知道通道是否可执行操作,例如:

如果readable为真,表示通道可读。SelectionKey提供了一些快捷方法专门来检测通道的状态,它们是:

  • isReadable()    相当于selectionKey.readyOps() & SelectionKey.OP_READ
  • isWritable()     相当于selectionKey.readyOps() & SelectionKey.OP_WRITE
  • isConnectable()    相当于selectionKey.readyOps() & SelectionKey.OP_CONNECT
  • isAcceptable()    相当于selectionKey.readyOps() & SelectionKey.OP_ACCEPT

创建Selector对象

可以通过两个方法创建Selector的对象实例。第一种是使用Selector对象提供的静态方法open();第二种是通过SelectorProvider对象的openSelector()方法。不过,当你查看Selector的open方法的源码,发现它实际上是使用了第二种方法返回的selector对象。

根据不同的平台,open方法返回的实现不同,在windows平台下,open返回的selector对象是WindowsSelectorImpl类型,而在Linux平台,返回的则是EPollSelectorImpl类型。

使用Selector对象

使用Selector对象的时候,首先注册channel,然后就可以调用Selector对象的select()方法开始工作。

select()方法返回的是已就绪的通道个数。

selectedKeys集合

Selector对象内部维护一个selectedKyes集合属性,一旦已注册通道有就绪条件,这个集合就会被更新。可以通过遍历这个集合来对每个就绪的通道进行操作。例如:

Selector的工作流程图

Selector的工作流程分两部走,第一步是注册过程,第二步是监听过程;

channel的注册过程

通过查看Selector的源码实现,简单总结一下Selector的工作流程,先来看Channel的注册过程:

channel注册过程

在注册之前,selector对象必须存在,因此把selector对象的创建放在第一位。在channel的register方法中,实际上是调用了selector的register方法,并把自己(this)作为其中一个参数传递过去,如下代码所示:

register channel

当selector的register方法执行完毕,Selector类中的keys字段也被初始化完成,keys字段包含所有被注册的channel对象的信息。

selector的监听过程

当selector对象的select()方法被执行,selector的监听过程就开始进行。select方法是selector对象的核心,当select方法发现有channel对象就绪时,就会更新它内部的selectedKeys属性,这个属性表示当前就绪通道的对象集合。

select过程

完整代码

下面的代码中selector分别对两个通道进行监控,一个ServerSocketChannel服务器通道,用于接受客户端连接;另一个是SocketChannel,它是一个客户端连接。

转载请注明:Pure nonsense » Java NIO 使用selector进行IO复用

喜欢 (1)
0 0 投票数
文章评分
订阅评论
提醒
guest
0 评论
内联反馈
查看所有评论
0
希望看到您的想法,请您发表评论x
()
x