Java NIO-Selector

Selector(选择器)是Java NIO中能够同时监测多个Channel通道,并且还能知道Channel上读写事件是否准备好。这样一个Selector线程就可以管理多个Channel,而不像Blocking IO那样一个线程对应一个监管一个IO事件。

Selector特点

一个Selector对应多个Channel:仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通道。事实上,可以只用一个线程处理所有的通道。对于操作系统来说,线程之间上下文切换的开销很大,而且每个线程都要占用系统的一些资源(如内存)。因此,使用的线程越少越好。

select、poll、epoll模式

select,poll,epoll都是IO多路复用的机制。I/O多路复用就通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

  1. select模式
    特点:忙轮询,文件描述符有大小限制,会将整个文件描述符数组频繁在用户内存空间和内核的内存地址空间的拷贝复制,造成较大的性能消耗。

过程:不停地从头到尾遍历整个文件描述符,根据每一个描述符的状态进行通知处理,被通知的线程还需要遍历整个文件描述符来判断是哪个事件准备好了。

  1. poll模式
    特点:轮询,没有文件描述符大小限制(有系统内存大小相关),但是同样会将整个文件描述符数组频繁在用户内存空间和内核的内存地址空间的拷贝复制,造成较大的性能消耗

过程:为了避免CPU空转,可以同时观察许多流的I/O事件,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中醒来,被唤醒的程序就会轮询一遍所有的文件描述符,来判断哪个IO的事件就绪,并进行相应的处理。

  1. epoll模式
    特点:基于IO事件响应,高性能,没有文件描述符大小限制,只会将准备就绪的IO描述符进行复制。

过程:epoll同时观察许多流的I/O事件,在没有IO事件发生时,会把当前线程阻塞掉,当有一个或多个流有I/O事件时,就从阻塞态中醒来,被唤醒的程序就能获取所有已经准备就绪的文件描述符,并进行相应的处理。

  1. epoll模式的其他特性
  • 在Linux2.6(包括)之后使用epoll模式实现Java NIO,在用户程序使用上是没有差别的,只是遍历Selector.selectedKeys()方法返回集合的时间复杂度为O(n)或O(1),其中n为整个文件描述符的数量。
  • epoll模式的高效性:epoll在被内核初始化时(操作系统启动),同时会开辟出epoll自己的内核高速cache区,用于安置每一个我们想监控的socket,这些socket会以红黑树的形式保存在内核cache里,以支持快速的查找、插入、删除。这个内核高速cache区,就是建立连续的物理内存页,然后在之上建立slab层,简单的说,就是物理上分配好你想要的size的内存对象,每次使用时都是使用空闲的已分配好的对象,避免了内核内存空间和用户内存空间的复制消耗。

Selector的使用

Selector总是和Channel成对出现的,与Selector一起使用时,Channel必须处于非阻塞模式下。这意味着不能将FileChannel与Selector一起使用,因为FileChannel不能切换到非阻塞模式。而套接字通道都可以。

从一个完整的程序示例开始


需要留意的步骤:

步骤3

使用Selector选择器需要将Channel设置为非阻塞状态,FileChannel不可设置为非阻塞状态,套接字可以。

步骤5

注意通常不会注册写就绪事件,因为在发送缓冲区未满的情况下始终是可写的,而且
注册写事件,而又不用写数据,则缓冲区未满总会响应写事件就绪,很容易造成CPU空转,出现消耗CPU100%的情况
注册的事件可以为:

  • SelectionKey.OP_CONNECT(某个channel成功连接到另一个服务器称为“连接就绪”)
  • SelectionKey.OP_ACCEPT(channel准备好接收新进入的连接称为“接收就绪”,对应于ServerSocket.accept方法)
  • SelectionKey.OP_READ(一个有数据可读的通道可以说是“读就绪”)
  • SelectionKey.OP_WRITE(等待写数据的通道可以说是“写就绪”)

register()方法会返回一个SelectionKey对象

  • interest集合(所选择的感兴趣的事件集合)
  • ready集合(通道已经准备就绪的操作的集合)

这是一个复合int类型字段,通过如下方法可以进行判断
selectionKey.isAcceptable();
selectionKey.isConnectable();
selectionKey.isReadable();
selectionKey.isWritable()

  • Channel、Selector

通过如下方法获取触发此IO事件的Channel,以及被哪个Selector监听到的

  • 附加的对象(可选,是在注册Channel时一同注册的一个对象)
步骤6.1

通过select方法获取通道

select()方法返回的int值表示有多少通道已经就绪。亦即,自上次调用select()方法后有多少通道变成就绪状态。如果调用select()方法,因为有一个通道变成就绪状态,返回了1,若再次调用select()方法,如果另一个通道就绪了,它会再次返回1。如果对第一个就绪的channel没有做任何操作,现在就有两个就绪的通道,但在每次select()方法调用之间,只有一个通道就绪了。

  • int select()

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

  • int select(long timeout)

select(long timeout)和select()一样,除了最长会阻塞timeout毫秒(参数)

  • int selectNow()

selectNow()不会阻塞,不管什么通道就绪都立刻返回

如何唤醒阻塞在select方法的线程

  • Selector.wakeup()

将使得选择器上的第一个有就绪Channel但是还没有返回的选择操作立即返回。如果当前没有就绪Channel被选择,那么下一次对 select( )方法的一种形式的调用将立即返回,后续的选择操作将正常进行。在选择操作之间多次调用 wakeup( )方法与调用它一次没有什么不同。有时这种延迟的唤醒行为并不是您想要的,您可以通过在调用 wakeup( )方法后调用 selectNow( )方法来绕过这个问题,此时需要将代码合理地关注于返回值和执行选择集合。

  • Selector.close( )

如果选择器的 close( )方法被调用,那么任何一个在选择操作中阻塞的线程都将被唤醒,与选择器相关的通道将被注销,而键将被取消。通道本身并不会关闭。

  • Thread.interrupt( )

抛出中断异常,程序异常返回,如果捕获异常则进行清理操作。

步骤6.3

每次迭代末尾的keyIterator.remove()调用。

Selector不会自己从已选择键集中移除SelectionKey实例。必须在处理完通道时自己移除。下次该通道变成就绪时,Selector会再次将其放入已选择键集中。实际上在两次调用select( )方法之间,都必须手动将其清空,换句话说,select( )方法只会在已有的所选键集上添加键,它们不会创建新的建集。

步骤7

先使Selector的注册信息失效,然后在关闭Channel

先关闭Selector,然后在关闭Channel,平滑的关闭整个系统。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,264评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,549评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,389评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,616评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,461评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,351评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,776评论 3 387
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,414评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,722评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,760评论 2 314
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,537评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,381评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,787评论 3 300
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,030评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,304评论 1 252
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,734评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,943评论 2 336

推荐阅读更多精彩内容