NIO中的选择器(Selector)的做用就是维护注册到选择器中的通道集合,每个通道与选择器的关系封装在选择键(SelectionKey)中,实际上能够认为选择器维护的是选择键集合。建立Selector对象使用Selector.open()。java
Selector类主要维护三个集合:安全
1. Registered key set(已注册键集合):调用Selector的keys()方法能够获取服务器
2. Selected key set(已选择键集合):调用Selector的selectedKeys()方法获取多线程
3. Cancelled key set(已取消键集合):已取消键集合是Selector对象的私有成员,外部没法访问并发
只有继承了SelectableChannel的类才能注册到选择器中,而且只有非阻塞模式的通道才能注册到选择器,以下代码:socket
//建立一个套接字服务器,并注册到选择器 //建立选择器 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循环轮询获取注册到选择器中通道选择器的操做,以下代码:ide
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()方法。rest
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:已注册的键集合是只读,修改它会抛出此异常