zoukankan      html  css  js  c++  java
  • 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实例无效。通道本身并不会关闭。

  • 相关阅读:
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    jQuery火箭图标返回顶部代码
    别傻傻不知道 == 和 equals 的区别【面试系列】
    Spring Boot(九):定时任务
  • 原文地址:https://www.cnblogs.com/dongguacai/p/5813430.html
Copyright © 2011-2022 走看看