NIO自从JDK1.4版本以来就添加的一个非阻塞I/O框架,NIO是Java为解决网络通讯中高并发问题的一个类库,Selector是java NIO的一个组件,用于检查一个或多个NIO Channel的状态是否处于可读、可写。如此可以实现单线程管理多个channels,也就是可以管理多个网络链接,所以Selecotr是实现了多路复用的关键。
为什么要使用Selector呢?
用单线程处理多个channels的好处是我需要更少的线程来处理channel。实际上,你甚至可以用一个线程来处理所有的channels。从操作系统的角度来看,切换线程开销是比较昂贵的,并且每个线程都需要占用系统资源,因此使用的线程自然是越少越好。
需要留意的是,现代操作系统和CPU在多任务处理上已经变得越来越好,所以多线程带来的影响也越来越小。如果一个CPU是多核的,如果不执行多任务反而是浪费了机器的性能。不过这些设计讨论是另外的话题了。简而言之,通过Selector我们可以实现单线程操作多个channel。
这有一幅示意图,描述了单线程处理三个channel的情况:
图一从图中我们可以看到,一个线程通过使用Selector可以同时管理3个Channel,那么如何实现这种管理的呢?我们先来看看Selector的定义:选择器(Selector)是SelectableChannle 对象的多路复用器,Selector 可以同时监控多个SelectableChannel 的IO 状况。不同于BIO(阻塞式I/O),Selector通过将channel注册到相应的集合中,在注册时使用一个SelectionKey来表示当前channel的状态,从而实现管理多个channel
创建一个selector的方法:
Selector selector=Selector.open();//创建一个Selector是通过Selector提供的open方法实现的。
注册Channel到Selector上,使得Selector可以管理Channel
channel.configureBlocking(false); //将阻塞设置为非阻塞
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
第一句代码是将channel转换为非阻塞模式,这一句是必不可少的,第二句代码在的使用方法有点反人类,按照我们的思维应该使用selector添加一个channel,但是代码中是将一个chanel注册到一个selector中,所以在编写代码是应该注意一下,这个时候我们能够发现第二个参数SelectionKey.OP_READ,这个表示了当前channel的状态,我们来看看SelectionKey提供的其他几种状态:
1.Connect //此时处于“连接就绪”状态
2.Accept //此时处于“可连接就绪”状态
3.Read //此时处于“读就绪”状态
4.Write //此时处于“写就绪”状态
这四种状态是用来表示channel的,在SelctionKey中他们分别对应:
1.SelectionKey.OP_CONNECT
2.SelectionKey.OP_ACCEPT
3.SelectionKey.OP_READ
4.SelectionKey.OP_WRITE
若注册时不止监听一个事件,则可以使用“位或”操作符连接。
channel.register(selector, SelectionKey.OP_READ|SelectionKey.OP_WRITE);
图二看完了SelectionKey对象,我们再回过头来看看我们的Selector类,类中一个方法keys(),该方法返回的是一个SelectionKey类型的Set集合,该方法能够将所有状态为OP_CONNECT的SelectionKey集合,也就是返回所有接入Selector的channel,我们可以通过SelectionKey中的channel方法获得到Channel对象,这样的话,我们就获得了Seelectoe管理的所有Channel对象。有了这个对象我们自然可以完成很多东西,关于这方面的应用我就不多叙述了。
Selector提供了一个select()方法,该方法返回所有可用的channel数量。
Selector还提供了selectedKeys()方法,用于获得所有可用的channel。
关于其他方法大家有兴趣可以看JDK的源码进行深一步的了解。
网友评论