Java NIO笔记(八):选择器

来源:互联网 发布:seo自学 编辑:程序博客网 时间:2024/04/30 06:26

        NIO中的选择器(Selector)的作用就是维护注册到选择器中的通道集合,每一个通道与选择器的关系封装在选择键(SelectionKey)中,实际上可以认为选择器维护的是选择键集合。创建Selector对象使用Selector.open()。

        Selector类主要维护三个集合:

  • Registered key set(已注册键集合):调用Selector的keys()方法可以获取
  • Selected key set(已选择键集合):调用Selector的selectedKeys()方法获取
  • Cancelled key set(已取消键集合):已取消键集合是Selector对象的私有成员,外部无法访问

        只有继承了SelectableChannel的类才能注册到选择器中,并且只有非阻塞模式的通道才能注册到选择器,如下代码:

//创建一个套接字服务器,并注册到选择器//创建选择器Selector selector = Selector.open();//创建Socket服务器通道ServerSocketChannel ssc = ServerSocketChannel.open();//绑定65535端口ssc.socket().bind(new InetSocketAddress(65535));//设置通道为非阻塞模式ssc.configureBlocking(false);//将通道注册到选择器,指定通道兴趣是等待接收连接SelectionKey key = ssc.register(selector, SelectionKey.OP_ACCEPT);

        实际使用中,一般使用while循环轮询获取注册到选择器中通道感兴趣的操作,如下代码:

while (true) {int n = selector.select();if (n > 0) {Iterator<SelectionKey> iter = selector.selectedKeys().iterator();while (iter.hasNext()) {SelectionKey keyy = iter.next();iter.remove();// ......}}}
        select()方法的作用是选择注册到选择器中通道感兴趣的键,此方法是阻塞的,直到有感兴趣的事件发生;还可以使用select(10000)方法设置选择键的超时时间,单位是Millisecond;还可以使用selectNow(),此方法是非阻塞,若没有通道就绪会立即返回0。

        不需要使用Selector时,调用close()方法可以关闭选择器,关闭选择器后,所有注册到其中的选择键会被设置为无效状态。

        关闭选择器后,试图调用它的方法会抛出ClosedSelectorException,这是一个非运行时异常,所有在使用时有必要检查选择器是否打开,使用isOpen()方法。

        NIO中定义了4中可选择操作:OP_READ(读)、OP_WRITE(写)、OP_CONNECT(连接)、OP_ACCEPT(接受),这些常量在SelectionKey中定义。可以使用通道的validOps()获取某个通道上支持的操作集合。

        同一个通道可以被多次注册到同一个选择器中,没有异常错误,也没有特别的效果,个人觉得这个地方设计的不严谨;实际使用中应避免同一个通道多次注册到一个选择器中,虽然对实际功能没有影响,但是为了整体思路清晰最好不要这么做。

        注册一个通道时可以在这个选择键上设置一个Object对象,比如在接受连接操作中设置,在读操作中获取,如下代码:

SelectionKey keyy = iter.next();iter.remove();if(keyy.isAcceptable()){ServerSocketChannel channel = (ServerSocketChannel) keyy.channel();SocketChannel sc = channel.accept();sc.register(selector, SelectionKey.OP_READ, "hello");}if(keyy.isReadable()){//attach的值为"hello"Object attach = keyy.attachment();SocketChannel sc = (SocketChannel) keyy.channel();}
        SelectionKey(选择键对象)提供了下面方法:

  • channel():获取关联的通道(SelectableChannel)
  • selector():获取关联的选择器(Selector)
  • isValid():验证维护选择器与通道关系的SelectionKey是否有效
  • cancel():取消键,调用一个已取消的键的方法将抛出CancelledKeyException
  • interestOps():获取这个key的兴趣(可选择操作)集合
  • interestOps(int ops):设置这个key的兴趣
  • readyOps():返回这个key准备好的操作集合(兴趣集合)
  • isReadable():检查选择键的兴趣是否为可读,实际上是通道的兴趣,选择键维护通道与选择器关系
  • isWritable():检查选择键的兴趣是否为可写
  • isConnectable():检查选择键的兴趣是否为连接
  • isAcceptable():检查选择键的兴趣是否为接受
  • attach(Object ob):设置一个Object数据到此key上
  • attachment():获取设置的Object数据

        可以设置null来清楚SelectionKey的附件对象,如果SelectionKey的存在周期很长,但是附件对象不需要存在很长,一定要再使用完后即时清理附件对象,否则附件对象不能被GC回收,可能会发生内存泄露。

        停止选择过程可以使用Selector的wakeup()方法,此方法可以安全的退出select()阻塞,

        选择器对象(Selector)是线程安全的,在多线程并发访问不会有问题。

        Selector(选择器)提供了下面方法:

  • open():打开一个选择器
  • isOpen():检查一个选择器实例是否打开
  • provider():返回一个SelectorProvider
  • keys():返回注册键集合
  • selectedKeys():返回已选择键集合
  • selectNow():立刻执行选择,非阻塞,若没有已准备好的通道则立即返回0
  • select(long timeout):执行选择,超过指定毫秒数则返回
  • select():执行选择,会一直阻塞直到有准备就绪的通道
  • wakeup():停止选择
  • close():关闭选择器

        使用Selector类可能会抛出的错误:

  • IllegalBlockingModeException:注册一个阻塞状态的通道抛出此异常
  • ClosedChannelException:注册一个已关闭的通道抛出此异常
  • ClosedSelectorException:调用一个已关闭的选择器的方法抛出此异常
  • UnsupportedOperationException:已注册的键集合是只读,修改它会抛出此异常
0 0
原创粉丝点击