Java NIO选择器

Posted 羽觞醉月

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java NIO选择器相关的知识,希望对你有一定的参考价值。

Selector选择器是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样使得一个单独的线程可以管理多个Channel,从而管理多个网络连接。

为什么使用Selector

对于操作系统来说,线程之间上下文切换的开销很大,而且每个线程都要占用系统的资源,所以使用Selector单独管理多个Channel

Selector的创建

Selector selector = Selector.open();

 

向Selector注册通道

channel.configureBlocking(false);
SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_READ);

 

与Selector一起使用时,通道必须处于非阻塞模式,这意味着FileChannel不能与Selector一起使用。套接字通道都可以

register()方法的第二个参数是一个“interest集合”,意思是通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件,用四种常量表示:

  • SelectionKey.OP_CONNECT: 连接就绪(某个Channel成功连接到另一个服务器)
  • SelectionKey.OP_ACCEPT: 接收就绪(一个ServerSocketChannel准备好接收新进入的连接)
  • SelectionKey.OP_READ: 读就绪(一个有数据可读的通道)
  • SelectionKey.OP_WRITE: 写就绪(等待写数据的通道)

通道触发了一个事件,就表示该事件已经就绪。如果对不止一种事件感兴趣,可以用位或操作符将常量连接:

int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

 

SelectionKey

这个对象包含了一些属性:

  • interest集合
  • ready集合
  • Channel
  • Selector
  • 附加的对象(可选)

interest集合

interest集合是你所选择的感兴趣的事件集合。可以通过SelectionKey读写interest集合

int interestSet = selectionKey.interestOps();
boolean isAccept = (interest & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
boolean isConnect = (interest & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT;
boolean isRead = (interest & SelectionKey.OP_READ) == SelectionKey.OP_READ;
boolean isWrite = (interest & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE;

 

用位与操作interest集合和SelectionKey常量可以判断某个确定的事件是否在interest集合中

ready集合

ready集合是通道已经准备就绪的操作的集合。在一次选择(Selection)之后,会首先访问ready set。

int readySet = selectionKey.readyOps();

 

可以像检测interest集合一样,检测Channel中有哪些事件或操作已经准备就绪,也可以用以下方法:

boolean isAccept = selectionKey.isAcceptable();
boolean isConnect = selectionKey.isConnectable();
boolean isRead = selectionKey.isReadable();
boolean isWrite = selectionKey.isWriteable();

 

Channel和Selector

Channel channel = selectionKey.channel();
Selector selector = selectionKey.selector();

 

附加对象

可以将一个对象或更多信息附加到SelectionKey上,这样能方便识别某个通道。例如,可以附加与通道一起使用的Buffer,或是包含聚集数据的某个对象

selectionKey.attach(object);
Object obj = selectionKey.attachment();

 

还可以用register()方法向Selector注册Channel时附加对象

SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_READ, object);

 

通过Selector选择通道

一旦向Selector注册了一个或多个通道,就可以调用几个重载的select()方法。这些方法会返回你所感兴趣的事件已经准备就绪的通道。

  • int select()
  • int select(long timeout)
  • int selectNow()

select()方法会阻塞到至少有一个通道在你注册的事件上就绪了

select(long timeout)和select()一样,除了最长会阻塞timeout毫秒

selectNow()不会阻塞,不管什么通道就绪都立刻返回

select()方法返回的int值表示有多少通道已经就绪,即自从上次调用select()方法后有多少通道变成了就绪状态。如果调用一次select()方法,因为有一个通道变成了就绪状态,返回1,如果再次调用select()方法,如果另一个通道也就绪了,则还会返回1。即使,第一个就绪的通道没有做任何操作,但是每次select()方法调用之间,只有一个通道变成就绪状态

selectedKeys()

一旦调用了select()方法,且返回值表明有一个或多个通道就绪了,然后可以调用Selector的selectedKeys()方法,访问“已选择键集“(selected key set)中的就绪通道

Set selectedKeys = selector.selectedKeys();

 

当向Selector注册Channel时,Channel.register()方法会返回一个SelectionKey对象。这个对象代表了注册到该Selector的通道。可以通过Selector的selectedKeys()方法访问这些对象

wakeUp()

某个线程调用select()方法之后阻塞了,即使没有通道已经就绪,也可以让其返回。只要让其他线程在第一个线程调用select()方法的对象上调用Selector.wakeUp()方法即可,阻塞在select()方法上的线程会立马返回

如果有其他线程调用了wakeUp()方法,且线程调用select()方法没有阻塞,下次调用select()方法的线程会立即醒来

close()

用完Selector后调用close()方法关闭,且是注册到Selector上的所有SelectionKey实例无效,通道本身并不会关闭

 






以上是关于Java NIO选择器的主要内容,如果未能解决你的问题,请参考以下文章

Java NIO5:选择器1---理论篇

Java NIO5:选择器1---理论篇

Java NIO系列4:通道和选择器

Java NIO选择器

Java Nio注意事项

七Java NIO 选择器