Java NIO选择器

  A Selector是一个Java NIO组件,可以检查一个或多个NIO通道,并确定哪些通道已准备就绪,例如读取或写入。这样一个线程可以管理多个通道,从而管理多个网络连接。

为什么选择器?

  使用单个线程来处理多个通道的优点是您需要较少的线程来处理通道。你可以使用一个线程来处理你所有的频道。线程之间的切换消耗系统资源较大,每个线程也占用操作系统中的一些资源(内存)。所以你使用的线程越少越好。

  现代操作系统和CPU在多任务处理中变得越来越好,所以随着时间的推移,多线程的开销越来越小。事实上,如果CPU具有多个内核,处理少量任务时会浪费CPU电源。

  这是一个线程使用Selector来管理3个通道的流程图:

Java NIO:A Thread uses a Selector to handle 3 Channel's

创建选择器

  Selector通过调用Selector.open()方法 创建一个,如下所示:

Selector selector= Selector.open();

为选择器注册通道

  为了使用一个ChannelSelector你必须注册ChannelSelector。这是使用SelectableChannel的register()方法完成的 ,如下所示:

channel.configureBlocking(false);

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

  Channel必须在非阻塞模式与Selector使用。这意味着你不能将FileChannelSelector一起使用,因为 FileChannel不能切换到非阻塞模式。Socket channels可以正常工作。

  注意register()方法的第二个参数。这是一个“兴趣集”,意思是你可以通过Selector在通道监听你需要的事件,。你可以监听四种不同的事件:

  • Connect
  • Accept
  • Read
  • Write

  “第一事件”的频道也被称为“ready”的事件。因此,已成功连接到其他服务器的通道是“connect ready”。接受传入连接的服务器套接字通道是“Accept”ready。具有准备读取的数据的通道“read”ready。准备好向您写入数据的通道是“write”ready。

这四个事件由四个SelectionKey常量表示:

  1. SelectionKey.OP_CONNECT
  2. SelectionKey.OP_ACCEPT
  3. SelectionKey.OP_READ
  4. SelectionKey.OP_WRITE

如果您对多个事件感兴趣,可以将常数在一起使用,如下所示:

int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE; 

SelectionKey‘s

  当您Channel使用Selector 该register()方法注册时返回一个SelectionKey对象。此SelectionKey对象包含一些有趣的属性:

  • The interest set
  • The ready set
  • The Channel
  • The Selector
  • An attached object (optional)

The interest set   ------  兴趣集

  兴趣集是您感兴趣的“选择”事件集。可以通过以下方式读取和写入该兴趣集SelectionKey

int interestSet = selectionKey.interestOps();

boolean isInterestedInAccept = interestSet&SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet&SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet&SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet&SelectionKey.OP_WRITE;

  可以看到将设置给定SelectionKey常数的选择兴趣集,以确定某个事件是否在兴趣集中。

The ready set      -----就绪集

  准备集是通道准备好的一组操作。您将主要在选择后访问就绪集。选择在后面的部分进行说明。

  访问就绪集代码展示:

int readySet = selectionKey.readyOps();

  您可以以与兴趣集相同的方式测试,频道准备就绪的事件/操作。但是,您也可以使用这四种方法,这些方法都返回布尔值:

selectionKey.isAcceptable();
selectionKey.isConnectable();
selectionKey.isReadable();
selectionKey.isWritable();

Channel + Selector      ----频道+选择器

  访问通道+选择器SelectionKey是不重要的。

   具体操作如下:

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

attached object (optional)---附加对象

  您可以附加一个对象,SelectionKey这是一种方便识别给定频道或将更多信息附加到频道的方法。例如,您可以附加Buffer您正在使用的频道,或者包含更多聚合数据的对象。以下是附加对象的方法:

selectionKey.attach(theObject);

Object attachedObj = selectionKey.attachment();

  您也可以附上已经一个对象,而注册Channel用 Selector,在register()方法。这是怎么样的样子:

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

通过选择器选择频道

  一旦您注册了一个或多个渠道,Selector您可以调用其中一种select()方法。这些方法返回对您感兴趣的事件(连接,接受,读取或写入)“准备好”的通道。换句话说,如果您对准备阅读的频道感兴趣,您将收到准备从select()方法中阅读的频道。

以下是select()方法:

  • int select()
  • int select(长时间超时)
  • int selectNow()

  select() 阻止至少一个通道为您注册的事件准备好。

  select(long timeout)select()除了它阻塞最多timeout毫秒(参数)外,还是一样的。

  selectNow()根本不阻止 它立即返回任何通道准备就绪。

  在int由返回的select()方法告诉很多渠道如何准备。也就是说,自从你上次打电话以来,已经有多少个频道准备好了select()。如果您打电话select(),并且返回1,因为一个通道已经准备就绪,并且再次拨打select()一个通道,并且还有一个通道已经准备就绪,它将再次返回1。如果您准备好的第一个频道没有任何功能,您现在可以有2个准备好的频道,但每个select()通话之间只有一个频道已经准备就绪。

selectedKeys()

  一旦您调用了一种select()方法,其返回值表示一个或多个通道已准备好,您可以通过调用选择器selectedKeys()方法通过“选定的键集”访问就绪通道。这是怎么样的样子:

设置<SelectionKey> selectedKeys = selector.selectedKeys();

  当你注册一个通道SelectorChannel.register()方法返回一个SelectionKey对象。该键表示通道与该选择器的注册。这些键可以通过该selectedKeySet()方法访问。从 SelectionKey

您可以迭代此选择的密钥集以访问就绪通道。这是怎么样的样子:

Set<SelectionKey> selectedKeys = selector.selectedKeys();

keyIterator = selectedKeys.iterator();

while(keyIterator.hasNext()){

    SelectionKey key = keyIterator.next();

    if(key.isAcceptable()){
//连接被ServerSocketChannel接受。 } else if(key.isConnectable()){
//与远程服务器建立连接。 } else if(key.isReadable()){
//一个频道准备好阅读 } else if(key.isWritable()){
//一个频道准备好写作了
} keyIterator.remove();
}

  该循环遍历所选密钥集中的密钥。对于每个密钥,它测试密钥以确定密钥引用的信道准备好了。

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

  该SelectionKey.channel()方法返回的通道应该被转换为您需要使用的通道,例如ServerSocketChannel或SocketChannel等。

wakeup()方法

  调用select()被阻止的方法的线程select(),即使没有通道尚未准备就可以离开该方法。这是由具有不同的线程调用完成Selector.wakeup() 的方法,Selector其中第一线呼吁 select()上。线程等待内线select()然后立即返回。

  如果一个不同的线程调用wakeup(),并且当前没有线程被阻塞select(),那么调用的下一个线程select()将立即“唤醒”。

close()方法

  当你完成Selector你调用它的close() 方法。这关闭并使所Selector注册的所有SelectionKey 实例无效Selector。渠道本身没有关闭。

全选择器示例

  这是一个打开一个完整的例子Selector,注册一个通道(通道实例化被忽略),并且继续监视Selector 四个事件(接受,连接,读取和写入)的“准备”。

Selector selector = Selector.open();

channel.configureBlocking(false);

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

while(true){

  int readyChannels = selector.select();

  if(readyChannels == 0)继续;

  Set<SelectionKey> selectedKeys = selector.selectedKeys();

  keyIterator = selectedKeys.iterator();

  while(keyIterator.hasNext()){

    SelectionKey key = keyIterator.next();

    if(key.isAcceptable()){
//连接被ServerSocketChannel接受。 } else if(key.isConnectable()){
//与远程服务器建立连接。 } else if(key.isReadable()){
//一个频道准备好阅读 } else if(key.isWritable()){
//一个频道准备好写作了
} keyIterator.remove();
}
}

Java NIO学习笔记四 NIO选择器的更多相关文章

  1. 零拷贝详解 Java NIO学习笔记四(零拷贝详解)

    转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...

  2. Java IO学习笔记四:Socket基础

    作者:Grey 原文地址:Java IO学习笔记四:Socket基础 准备两个Linux实例(安装好jdk1.8),我准备的两个实例的ip地址分别为: io1实例:192.168.205.138 io ...

  3. Java设计模式学习笔记(四) 抽象工厂模式

    前言 本篇是设计模式学习笔记的其中一篇文章,如对其他模式有兴趣,可从该地址查找设计模式学习笔记汇总地址 1. 抽象工厂模式概述 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问 ...

  4. Java基础学习笔记(四) - 认识final关键字、权限修饰符和内部类

    一.final关键字 为什么要使用 final 关键字? 通过继承我们知道,子类可以重写父类的成员变量和方法.final 关键字可以用于修饰父类,父类成员变量和方法,使其内容不可以被更改. 1.被修饰 ...

  5. Java NIO学习笔记九 NIO与IO对比

    Java NIO与IO Java nio 和io 到底有什么区别,以及什么时候使用nio和io,本文做一个比较. Java NIO和IO之间的主要区别 下表总结了Java NIO和IO之间的主要区别, ...

  6. NIO学习笔记四 :SocketChannel 和 ServerSocketChannel

    Java NIO中的SocketChannel是一个连接到TCP网络套接字的通道.可以通过以下2种方式创建SocketChannel: 打开一个SocketChannel并连接到互联网上的某台服务器. ...

  7. Java基础学习笔记四 Java基础语法

    数组 数组的需求 现在需要统计某公司员工的工资情况,例如计算平均工资.最高工资等.假设该公司有50名员工,用前面所学的知识完成,那么程序首先需要声明50个变量来分别记住每位员工的工资,这样做会显得很麻 ...

  8. java jvm学习笔记四(安全管理器)

    欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 前面已经简述了java的安全模型的两个组成部分(类装载器,class文件校验器),接下来学习的是java安全模型的另外一 ...

  9. 《Thinking in Java》学习笔记(四)

    1.Java中的闭包与回调 闭包(Closure)是一种能被调用的对象,它保存了创建它的作用域的信息.JAVA并不能显式地支持闭包,但是在JAVA中,闭包可以通过“接口+内部类”来实现,因为对于非静态 ...

随机推荐

  1. PyQt5多个GUI界面设计

    版权声明:本文为博主原创文章,转载 请注明出处:https://blog.csdn.net/sc2079/article/details/90454379 - 写在前面 本科毕业设计终于告一段落了.特 ...

  2. c线程使用锁控制并发

    // // Created by gxf on 2019/12/16. // #include <stdlib.h> #include <stdio.h> #include & ...

  3. Java常用数学类和BigDecimal

    笔记: Math类 * java.lang.Math提供了一系列静态方法用于科学计算:其方法的参数和返回值类型一般为double型. * abs 绝对值 * acos,asin,atan,cos,si ...

  4. ZZNU-OJ-2098 : Drink coffee【线段树合并区间或者 差分 + 二分索引树】

    : Drink coffee 时间限制: Sec 内存限制: MiB 提交: 答案正确: 提交 状态 讨论区 题目描述 为了在上课时保持清醒,凯伦需要一些咖啡.咖啡爱好者凯伦想知道最佳的温度来冲煮完美 ...

  5. 文件已经传输过来,数据在data,http接收

  6. linux 确定端口使用情况

    lsof -i:3533 确定端口已经在使用

  7. Python与数据库 sqlalchemy 建立声明层表对象的两种方式

    在对表对象进行建立的时候,通常有两种方式可以完成,以下是两种方式的建立过程对比 首先导入需要的模块,获取一个声明层 1 from sqlalchemy.sql.schema import Table, ...

  8. Nginx防盗链模块ngx_http_referer_module

    ngx_http_referer_module⽤用来阻⽌止Referer⾸首部⽆无有效值的请求访问,可防⽌止盗链指令:12.1 valid_referers定义 referer ⾸首部的合法可⽤用值, ...

  9. vue3.0脚手架 创建项目

    1.下载node最新稳定版本,并且安装 2.安装好之后,在cmd或者terminal下, 使用npm -v 查看当前npm版本,验证是否安装成功 3.安装成功后,运行 npm i -g @vue/cl ...

  10. Mongo Backup

    #!/bin/sh # This script is run on every mongo node. However, it checks to see if this node is the pr ...