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();

为选择器注册通道

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

channel.configureBlocking(false);

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

  Channel必须在非阻塞模式与Selector使用。这意味着你不能将FileChannel和Selector一起使用,因为 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();

  当你注册一个通道Selector的Channel.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学习笔记

    Java NIO学习笔记 一 基本概念 IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. 所有语言运行时系统提供执 ...

  2. Java NIO 学习笔记(七)----NIO/IO 的对比和总结

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  3. Java NIO 学习笔记(四)----文件通道和网络通道

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  4. Java NIO 学习笔记(三)----Selector

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  5. Java NIO 学习笔记(一)----概述,Channel/Buffer

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  6. Java:NIO 学习笔记-3

    Java:NIO 学习笔记-3 根据 黑马程序员 的课程 JAVA通信架构I/O模式,做了相应的笔记 3. JAVA NIO 深入剖析 在讲解利用 NIO 实现通信架构之前,我们需要先来了解一下 NI ...

  7. Java:NIO 学习笔记-1

    Java:NIO 学习笔记-1 说明:本笔记是根据bilibili上 尚硅谷 的课程 NIO视频 而做的笔记 主要内容 Java NIO 简介 Java NIO 与 IO 的主要区别 缓冲区(Buff ...

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

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

  9. Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

随机推荐

  1. web.xml 中配置了error-page但不起作用问题

    问题: 在web.xml 中配置了 error-page,但是好像不起作用,就是跳转不到指定的页面. 配置信息如下: <!-- 400错误 --> <error-page> & ...

  2. javascript执行原理

    执行环境 当执行流执行到函数时会创建一个执行环境,这个执行环境包含了函数内部 语句可以访问的所有变量和函数,当代码执行完时,销毁执行环境.所以一般情 况下,局部变量在函数执行完时会被销毁. 作用域.调 ...

  3. JDK版本不匹配...

    Java compiler level does not match the version of the installed Java project facet. 今天从把交通厅的项目,导进来就报 ...

  4. Vue H5 History 部署IIS上404问题

    背景简介 vue使用vue-router时,默认的地址并不美观,以#进行分割,例如:http://www.xxx.com/#/main. 为了访问地址能像正常的url一样,例如:http://www. ...

  5. C#基础知识-基本的流程控制语句(三)

    所谓的流程控制就是在程序运行中控制程序的走向,可以通过各种的条件判断执行代码的顺序,有if... if...else.. else...if |switch case...|while... Do.. ...

  6. 初次使用git配置以及git如何使用ssh密钥(将ssh密钥添加到github)

    初次安装git配置用户名和邮箱 初次安装git需要配置用户名和邮箱,否则git会提示:please tell me who you are. 你需要运行命令来配置你的用户名和邮箱: $ git con ...

  7. Spring Boot 之构建Hello Word项目

    1.创建一个maven项目 如下步骤: (第一步) (第二步) (第三步) 2.配置pom.xml文件 加载一些依赖包.字符集.指定jdk.编译插件. <project xmlns=" ...

  8. 学习笔记:HTML+CSS 基础知识

    1.<q>标签,短文本引用       <q>引用文本</q> <q>标签的真正关键点不是它的默认样式双引号(如果这样我们不如自己在键盘上输入双引号就行 ...

  9. 使用Cmder的几个问题

    Cmder 全尺寸版本 [101022] 新版本的 Cmder Full 版本,安装包目录的 config 目录下,已经没有 aliases 文件,在 vendor 下的 init.bat 下也没有了 ...

  10. nginx+tomcat集群负载均衡(实现session复制)

    转自:http://talangniao.iteye.com/blog/341512 架构描述 前端一台nginx服务器做负载均衡器,后端放N台tomcat组成集群处理服务,通过nginx转发到后面( ...