JAVA NIO系列(四) 选择器

  前面介绍过Channel、Buffer,后面的文章主要讲解Selector的实践以及实现原理,选择器的概念比起通道、缓冲区要复杂一些,并且选择器是NIO中最重要的一部分内容。

为什么使用Selector

  Selector又称为“选择器”,单个线程通过Selector可以管理多个SelectableChannel,实际应用中管理多个请求连接。对于操作系统来说,线程之间上下文切换的开销很大,而且每个线程都要占用系统的一些资源,比如内存,因此使用的线程越少越好。

                    

一、Selector的创建

Selector s = Selector.open();

二、通道注册

  channel.configureBlocking(false);
  SelectionKey key = channel.register(s, SelectionKey.OP_ACCEPT);

Channel与Selector配合使用,必须将通道注册到选择器上,通过调用SelectableChannel的register()方法。注册完成之后,会返回此通道向选择器注册的键。

注册的时候调用的是SelectableChannel,所以注册仅支持父类是SelectableChannel的通道类;另外通道要设置成非阻塞模式,所以FileChannel不能与Selector一起使用(因为FileChannel不能切换到非 阻塞模式),而socket通道可以

register(Selector sel,int ops)方法调用的是SelectableChannel的注册方法,其中第二个参数是一个interest集合,表示在通过Selector监听Channel时对什么事件感兴趣。事件分为以下四种:

1、Connect

2、Accept

3、Read

4、Write

通道触发了一个事件意思是该事件已经就绪:

1、某个channel成功连接到另一个服务器称为“连接就绪”;

2、一个server socket channel准备好接收新进入的连接称为“接收就绪”

3、一个有数据可读的通道可以说是“读就绪”

4、等待写数据的通道可以说是“写就绪”

三、SelectionKey

表示 SelectableChannelSelector 中的注册的标记,每次向选择器注册通道时就会创建一个选择键。选择键中包含的内容有:

1、interset集合

2、ready集合

3、Channel

4、Selector

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

int n = selector.select();

select方法返回的int值表示有多少通道已经就绪。第一次调用select方法,如果有一个通道就绪,则返回1;如果再次调用select方法,此时另一个通道就绪了,它会再次返回1。我们可以通过Selector的selectedKeys的方法,访问“已选择键集”中的就绪通道

selector.selectedKeys()
 1  while(iterator.hasNext())
 2             {
 3                 SelectionKey key = iterator.next();
 4                 //通道上是否有可接受的连接
 5                 if(key.isAcceptable())
 6                 {
 7                     ServerSocketChannel ssl = (ServerSocketChannel)key.channel();
 8                     SocketChannel scl = ssl.accept();
 9                     scl.configureBlocking(false);
10                     scl.register(selector, SelectionKey.OP_READ);
11                 }
12                 //通道上是否有数据可读
13                 else if(key.isReadable())
14                 {
15                     readDataFromSocket(key);
16                 }
17                 iterator.remove();
18             }

这个循环遍历键集中的每个键,并检测各个键对应的通道的就绪事件并做相应的处理。

我们要注意第17行,调用迭代器的remove方法。Selector不会自己从已选择键集中移除SelectionKey实例,必须要我们自己处理完通道时手动处理。下次该通道变成就绪时,Selector会再次将其放入已选择键集中。

四、WakeUp

某个线程调用select方法阻塞了,即使没有通道就绪,也有办法让其从select返回。只要让其它线程在第一个线程调用select方法的那个对象上调用selector.wakeup方法即可。阻塞在select方法上的线程立马返回。

五、Close

用完selector后调用其close方法会关闭Selector,且使其注册到该Selector上的所有SelectionKey实例无效。通道本身并不会关闭。

 

posted @ 2016-12-02 15:25  冬瓜蔡  阅读(1140)  评论(0编辑  收藏  举报