概述

  Selector一般称为选择器,也可以翻译为多路复用器,是Java NIO核心组件之一,主要功能是用于检查一个或者多个NIO Channel(通道)的状态是否处于可读、可写。如此可以实现单线程管理多个Channel(通道),当然也可以管理多个网络连接。

  使用Selector的好处在于,可以使用更少的线程来处理更多的通道,相比使用更多的线程,避免了线程上下文切换带来的开销等。

Selector(选择器)方法

1.Selector的创建

  通过调用静态工厂方法Selector.open()方法创建一个Selector对象。

Selector selector = Selector.open();

  open()方法实际上是向SPI1发出请求,通过默认的SelectorProvider对象获取一个新的Selector实例。

2.注册Channel到Selector

channel.configureBlocking(false);
SelectionKey key = channel.register(selector, Selectionkey.OP_READ);

  代码的第一句就是让这个Channel(通道)是非阻塞的。它是SelectableChannel抽象类里的方法,用于使通道处于阻塞模式或非阻塞模式,false表示非阻塞,true表示阻塞。它的签名是:

abstract SelectableChannel configureBlocking(boolean block)  

  要想Channel注册到Selector中,那么这个Channel必须是非阻塞的。所以FileChannel不适合Selector,因为FileChannel不能切换为非阻塞模式,更准确的说是因为FileChannel没有继承SelectableChannel。但是SocketChannel可以正常使用。

代码的第二行,register()方法就是将通道注册到Selector中,并且让Selector监听感兴趣的事件(第二个参数)。

着重讲一下第二个参数,它是一个“interest集合”,意思是在通过Selector监听Channel时对什么事件感兴趣。可以监听四种不同类型的事件:Connect、Accept、Read、Write。

  ❤ Connect:成功连接到另一个服务器称为“连接就绪”;

  ❤ Accept:ServerSocketChannel准备好接收新进入的连接称为“接收就绪”;

  ❤ Read:有数据可读的通道称为“读就绪”;

  ❤ Write:等待写数据的通道称为“写就绪”;

上面这四种事件用SelectionKey的四个常量来表示:

SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE

如果你对不止一种事件感兴趣,可以使用或( | )运算符来操作:

int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

3.SelectionKey

  一个SelectionKey键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系。

key.attachment(); //返回SelectionKey的attachment,attachment可以在注册channel的时候指定。
key.channel(); // 返回该SelectionKey对应的channel。
key.selector(); // 返回该SelectionKey对应的Selector。
key.interestOps(); //返回代表需要Selector监控的IO操作的bit mask
key.readyOps(); // 返回一个bit mask,代表在相应channel上可以进行的IO操作。

(1)key.interestOps():

  通过这个方法来判断Selector是否对Channel的某种事件感兴趣;

int interestSet = selectionKey.interestOps();
boolean isInterestedInAccept = (interestSet & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;

(2)key.readyOps():

  readySet 集合时通道已经准备就绪的操作的集合。Java中定义了以下几个方法来检查这些操作是否就绪:

   //创建ready集合的方法int readySet = selectionKey.readyOps();//检查这些操作是否就绪的方法selectionKey.isAcceptable();//等价于selectionKey.readyOps()&SelectionKey.OP_ACCEPT
    selectionKey.isConnectable();selectionKey.isReadable();selectionKey.isWritable();

(3)key.attachment():

  可以将一个对象或者更多信息附着到SelectionKey上,这样就能方便的识别某个特定的通道。例如,可以附加与通道一起使用的Buffer,或者包含聚集数据的某个对象。如:

    key.attach(theObject);Object attachedObj = key.attachment();

  还可以在register()方法使用的时候(即Selector注册Channel的时候)附加对象:

SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);

(4)key.channel()和key.selector() :

  取出SelectionKey关联的Channel和Selector;

    Channel channel = key.channel();Selector selector = key.selector();

Selector中的Channel

  选择器维护注册过的通道,这种选择器与通道的注册关系被封装在SelectionKey中。

public abstract class Selector
{...public abstract Set keys();public abstract Set selectedKeys();public abstract int select() throws IOException;public abstract int select(long timeout) throws IOException;public abstract int selectNow() throws IOException;public abstract void wakeup();...
}

Selector维护的三种类型SelectionKey集合:

(1)已注册的键的集合(Registered key set)

所有与选择器关联的通道所生成的键的集合称为已注册键的集合。这个集合通过keys()方法返回,并且有可能是空的。

注意:并不是所有注册过的键都有效。同时已注册键的集合是不可以直接修改的,若这么做的话,将会抛出ava.lang.UnsupportedOperationException 异常。

(2)已选择键的集合(Selected key set)

已注册键的集合的子集,这个集合的每个成员都是相关的通道被选择器判断为已经准备好的并且包含于键的interest集合中的操作。这个集合通过selectedKeys()方法返回(有可能是空的)。

注意:这些键可以直接从这个集合中移除,但是不能添加。若这么做的话将会抛出java.lang.UnsupportedOperationException异常。

(3)已取消键的集合(Cancelled key set)

已注册键的集合的子集,这个集合包含了cancel()方法被调用过的键(这个键已经被无效化),但他们还没有被注销。这个集合是选择器对象的私有成员,因而无法直接访问。

注意:当键被取消(可以通过isValid()方法来判断)时,它将被放在相关的选择器的已取消的键的集合里。注册不会立即被取消,但键会立即失效。当再次调用select()方法时(或者一个正在进行的select()调用结束时),已取消的键的集合中的被取消的键将会被清理掉,并且相应的注销也将会完成。通道会被注销,新的SelectionKey将被返回。当通道关闭时,所有相关的键会自动取消(一个通道可以被注册到多个选择器上)。当选择器关闭时,所有被注册到该选择器的通道都将被注销,并且相应的键将立即被无效化(取消),一旦键被无效化,调用它的与相关的方法就将抛出CancelledKeyException 异常。

select()方法

  在刚初始化的Selector对象中,上面讲述的三个集合都是空的。通过Selector的select()方法可以选择已经准备就绪的通道(这些通道包含你感兴趣的事件)。比如你对读就绪的通道感兴趣,那么select()方法就会返回读事件已经就绪的那些通道。下面是Selector重载的几个select()方法:

    ❤ int select():阻塞到至少有一个通道在你注册的事件上就绪了;

    ❤ int select(long timeout):和select()一样,但最长阻塞时间为timeout毫秒;

    ❤ int selectNow():非阻塞,执行就绪检查过程,但不阻塞,如果当前没有通道就绪,立刻返回0;

  select()方法返回的int值表示有多少通道已经就绪,是自上次调用select()方法后有多少通道变成就绪状态。之前在调用select()时进入就绪的通道不会在本次调用中被计入,而在前一次select()调用进入就绪但现在已经不在于就绪状态的通道也不会被计入。例如:首次调用select()方法,如果有一个通道变成了就绪状态,返回了1,若再次调用select()方法,如果一个另一个通道就绪了,它会再次返回1.如果对第一个就绪的Channel没有做任何操作,现在就有两个就绪的通道,但在每次select()方法调用之间,只有一个通道就绪了。

  一旦调用了select()方法,并且返回值不为0时,则可以通过调用Selector的selectedKeys()方法来访问已选择键的集合。如下:

 1 Set selectedKeys = selector.selectedKeys();
 2 Iterator keyIterator = selectedKeys.iterator();
 3 while(keyIterator.hasNext()) {
 4     SelectionKey key = keyIterator.next();
 5     if(key.isAcceptable()) {
 6         // a connection was accepted by a ServerSocketChannel.
 7     } else if (key.isConnectable()) {
 8         // a connection was established with a remote server.
 9     } else if (key.isReadable()) {
10         // a channel is ready for reading
11     } else if (key.isWritable()) {
12         // a channel is ready for writing
13     }
14     keyIterator.remove();
15 }

请注意keyIterator.remove()每次迭代结束时的呼叫。在Selector删除SelectionKey作为自己选择的关键实例,当你完成处理后,你必须这样做。这样的话才能在通道下一次变为“就绪”时,Selector将再次将其添加到所选的键集合。

停止选择

  选择器执行选择的过程,系统底层会一次询问每个通道是否就绪,这个过程可能会造成调用线程进入阻塞状态,那么我们有一下二种方式来唤醒在Select()方法中阻塞的线程。

(1)wakeup()方法:一个线程调用select()方法的那个对象上调用Selector.wakeup()方法。阻塞在select()方法上的线程会立马返回。如果有其它线程调用了wakeup()方法,但当前没有线程阻塞在select()方法上,下个调用select()方法的线程会立即“醒来(wake up)”。

(2)close()方法:该方法使得任何一个在选择操作中阻塞的线程都被唤醒,用完Selector后调用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效。通道本身并不会关闭。

转载于:https://www.cnblogs.com/Joe-Go/p/9970375.html

选择器(Selector)相关推荐

  1. java nio channel原理_Java NIO 选择器(Selector)与通道(Channel) 原理 | 学步园

    NIO底层实现poll, epoll(jdk1.5update 9  和jdk1.6  仅限于 linux 2.6以上 ) Java NIO 选择器(Selector) 知识预备 (linux epo ...

  2. Flume-NG源码阅读之SourceRunner,及选择器selector和拦截器interceptor的执行

    在AbstractConfigurationProvider类中loadSources方法会将所有的source进行封装成SourceRunner放到了Map<String, SourceRun ...

  3. 【Netty】NIO 选择器 ( Selector ) 通道 ( Channel ) 缓冲区 ( Buffer ) 网络通信案例

    文章目录 I . NIO 通信 服务器端 流程说明 II . NIO 通信 服务器端代码 III . NIO 通信 客户端 流程说明 IV . NIO 通信 客户端代码 V . NIO 通信 示例运行 ...

  4. 【Netty】NIO 选择器 ( Selector ) 简介

    文章目录 I . 选择器 ( Selector ) II . 选择器 ( Selector ) 与 NIO 特性 III . 选择器 ( Selector ) API 简介 IV . Selectio ...

  5. 【Netty】NIO 简介 ( NIO 模型 | NIO 三大组件 | 选择器 Selector | 通道 Channel | 缓冲区 Buffer | NIO 组件分配 | 缓冲区示例 )

    文章目录 I . NIO 模型 II . NIO 三大组件交互流程 III . NIO 缓冲区 IV . NIO 与 BIO 对比 V . NIO 线程分配 VI . 缓冲区 ( Buffer ) 示 ...

  6. Objective-C 2.0 with Cocoa Foundation--- 5,Class类型,选择器Selector以及函数指针

    5,Class类型,选择器Selector以及指针函数 系列讲座有着很强的前后相关性,如果你是第一次阅读本篇文章,为了更好的理解本章内容,笔者建议你最好从本系列讲座的第1章开始阅读,请点击这里 . 上 ...

  7. html选择器selector,网页解析神器-Selector选择器全面解析

    大部分人可能喜欢使用以下三个解析器对网页进行解析: BeautifulSoup:使用方便,支css选择器,但它有个不可忽视的缺点:慢. lxml解析库:采用xpath解析,速度快. pyquery:它 ...

  8. netty:NIO模型--选择器(Selector)

    1. java的NIO,用非阻塞的IO方式,可以用一个线程,处理多个的客户连接,就会使用到Selector(选择器). 2. Selector能够检测多个注册的通道上是否有事件发生(注意: 多个Cha ...

  9. java nio epoll_Java NIO 选择器(Selector)的内部实现(poll epoll)

    http://blog.csdn.net/hsuxu/article/details/9876983 之前强调这么多关于linux内核的poll及epoll,无非是想让大家先有个认识: Java NI ...

  10. 背景选择器selector替换按钮默认背景

    一.效果 正常状态 获取焦点或按下 按钮的背景图片是.9图,.9图的制作过程,见下面博文 http://blog.csdn.net/zengmingen/article/details/5019324 ...

最新文章

  1. Python爬虫--抓取糗事百科段子
  2. linux下使用 du查看某个文件或目录占用磁盘空间的大小
  3. 详细的mongo工具手册,包含常见CURD+条件操作+聚合+案例
  4. String/Stringbuilder/StringBuffer
  5. 实战演习-用wse上传下载文件
  6. Spark中的数据本地性
  7. Kafka设计解析(一)- Kafka背景及架构介绍
  8. maven的安装步骤
  9. ConcurrentLinkedQueue since java1.5
  10. 易语言大漠插件模块制作使用系统字库找字
  11. IPSAN 配置过程
  12. 论文记载:A Survey on Traffic Signal Control Methods
  13. 为什么我不推荐去SAP培训机构参加培训?
  14. 电脑登录宽带怎么自动连接服务器地址,宽带自动连接怎么设置
  15. Android基础学习笔记14:安卓手势编程
  16. JGG专刊征稿:时空组学
  17. Convex Optimization-chapter1
  18. 世界末日来了别怕!五种新技术抱紧你
  19. 正睿OI DAY8 ks1
  20. Unity editor 快速上手 quick start

热门文章

  1. mosh--sql教程个人笔记--基础
  2. 山财大计算机科学与工程学科排名,2016山东省大学一流学科排行榜 海大超山大|山东高考|一流学科|高校排行榜_新浪教育_新浪网...
  3. 为什么做 Java 开发的公司需要那么多程序员?
  4. Linux下如何运行bash文件和.sh文件
  5. 如何成为前端开发人员的10个技巧!以及前端学习路线
  6. 一点儿思考-如何做好一件事情
  7. yml 配置 mapper-locations 支持多级目录
  8. php高效轮询,PHP实现长轮询
  9. 面试官:你知道 LRU算法 —— 缓存淘汰算法吗?
  10. 投资研究——中信建投证券研究报告