深入了解java NIO之Selector(选择器)
这一节我们将探索选择器(selectors)。选择器提供选择执行已经就绪的任务的能力,这使得多元I/O成为可能。就像在第一章中描述的那样,就绪选择和多元执行使得单线程能够有效率地同时管理多个I/O通道(channels)。C/C++代码的工具箱中,许多年前就已经有select()和poll()这两个POSIX(可移植性操作系统接口)系统调用可供使用了。许过操作系统也提供相似的功能,但对Java程序员来说,就绪选择功能直到JDK1.4才成为可行的方案。
下面我们来使用选择器:
通过Selector.open()方法,我们可以创建一个选择器:
Selectorselector=Selector.open();
将Channel注册到选择器中:
channel.configureBlocking(false); SelectionKeykey=channel.register(selector,SelectionKey.OP_READ);
注意,如果一个Channel要注册到Selector中,那么这个Channel必须是非阻塞的,即channel.configureBlocking(false);因为Channel必须要是非阻塞的,因此FileChannel不能够使用选择器,因为FileChannel都是阻塞的.
注意到,在使用Channel.register()方法时,第二个参数指定了我们对Channel的什么类型的事件感兴趣,这些事件有:
- Connect,即连接事件(TCP连接),对应于SelectionKey.OP_CONNECT
- Accept,即确认事件,对应于SelectionKey.OP_ACCEPT
- Read,即读事件,对应于SelectionKey.OP_READ,表示buffer可读.
- Write,即写事件,对应于SelectionKey.OP_WRITE,表示buffer可写.
一个Channel发出一个事件也可以称为对于某个事件,Channel准备好了.因此一个Channel成功连接到了另一个服务器也可以被称为connectready.
我们可以使用或运算|来组合多个事件,例如:
intinterestSet=SelectionKey.OP_READ|SelectionKey.OP_WRITE;
注意,一个Channel仅仅可以被注册到一个Selector一次,如果将Channel注册到Selector多次,那么其实就是相当于更新SelectionKey的interestset.例如:
channel.register(selector,SelectionKey.OP_READ); channel.register(selector,SelectionKey.OP_READ|SelectionKey.OP_WRITE);
上面的channel注册到同一个Selector两次了,那么第二次的注册其实就是相当于更新这个Channel的interestset为SelectionKey.OP_READ|SelectionKey.OP_WRITE.
但是JavaNIO的selector允许一个单一线程监听多个channel输入。我们可以注册多个channel到selector上,然后然后用一个线程来挑出一个处于可读或者可写状态的channel。selector机制使得单线程管理多个channel变得容易。
下面我们写一个完整的例子,看一下Selector的用法:
//创建选择器 Selectorselector=Selector.open(); channel.configureBlocking(false); //注册通道 SelectionKeykey=channel.register(selector,SelectionKey.OP_READ); while(true){ //查看selector中的key是否准备好 intreadyChannels=selector.select(); //小于0超时,等于0没准备好,大于0已经准备完毕 if(readyChannels==0)continue; //获取选择器中的key SetselectedKeys=selector.selectedKeys(); Iterator keyIterator=selectedKeys.iterator(); while(keyIterator.hasNext()){ SelectionKeykey=keyIterator.next(); //遍历已选择键集中的每个键,并检测各个键所对应的通道的就绪事件 if(key.isAcceptable()){ //连接已经被ServerSocketChannel所接受 }elseif(key.isConnectable()){ //连接已经被远程终止. }elseif(key.isReadable()){ //通道已经准备好读数据 }elseif(key.isWritable()){ //通道已经准备好写数据 } keyIterator.remove(); } }
选择器的使用还有很多的细节,我们应该多查看api文档了解各个方法的用法。下一节我们做一个综合练习,总结一下NIO的使用。
以上就是深入了解javaNIO之Selector(选择器)的详细内容,更多关于javanioSelector(选择器)的资料请关注毛票票其它相关文章!