Java NIO学习笔记 NIO选择器
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常量
表示:
- SelectionKey.OP_CONNECT
- SelectionKey.OP_ACCEPT
- SelectionKey.OP_READ
- 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选择器的更多相关文章
- Java NIO学习笔记
Java NIO学习笔记 一 基本概念 IO 是主存和外部设备 ( 硬盘.终端和网络等 ) 拷贝数据的过程. IO 是操作系统的底层功能实现,底层通过 I/O 指令进行完成. 所有语言运行时系统提供执 ...
- Java NIO 学习笔记(七)----NIO/IO 的对比和总结
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(四)----文件通道和网络通道
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(三)----Selector
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java NIO 学习笔记(一)----概述,Channel/Buffer
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
- Java:NIO 学习笔记-3
Java:NIO 学习笔记-3 根据 黑马程序员 的课程 JAVA通信架构I/O模式,做了相应的笔记 3. JAVA NIO 深入剖析 在讲解利用 NIO 实现通信架构之前,我们需要先来了解一下 NI ...
- Java:NIO 学习笔记-1
Java:NIO 学习笔记-1 说明:本笔记是根据bilibili上 尚硅谷 的课程 NIO视频 而做的笔记 主要内容 Java NIO 简介 Java NIO 与 IO 的主要区别 缓冲区(Buff ...
- 零拷贝详解 Java NIO学习笔记四(零拷贝详解)
转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...
- Java NIO 学习笔记(六)----异步文件通道 AsynchronousFileChannel
目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...
随机推荐
- H5前端框架推荐合集
Ionic ionic 吧开发流程都帮你做好了,已经不再是单纯的UI框架,而是开发框架了,非常适合快速开发.基于angular2,丰富的UI组件,大大改进的编程模型, Semantic UI 中文官网 ...
- JQuery 中关于插入新元素的方法
关于JQuery插入新内容的方法: append() - 在被选元素的结尾插入内容 prepend() - 在被选元素的开头插入内容 after() - 在被选元素之后插入内容 before() - ...
- 【山东省选2008】郁闷的小J 平衡树Treap
小J是国家图书馆的一位图书管理员,他的工作是管理一个巨大的书架.虽然他很能吃苦耐劳,但是由于这个书架十分巨大,所以他的工作效率总是很低,以致他面临着被解雇的危险,这也正是他所郁闷的.具体说来,书架由N ...
- hdu1281二分图匹配
小希和Gardon在玩一个游戏:对一个N*M的棋盘,在格子里放尽量多的一些国际象棋里面的"车",并且使得他们不能互相攻击,这当然很简单,但是Gardon限制了只有某些格子才可以放, ...
- css小细节罗列
有空时候把一些常见可能不是每个人都知道的css小细节总结了下,共勉. 1.line-height 众多周知,line-height是行高的意思,我们时常会使用类似line-height:24px;这样 ...
- 一个大数据平台省了20个IT人力——敦奴数据平台建设案例分享
认识敦奴 敦奴集团创立于1987年,主营服装.酒店.地产,总部位于中国皮都-海宁.浙江敦奴联合实业股份有限公司(以下简称"敦奴")是一家集开发.设计.生产.销售于一体的大型专业服装 ...
- ubuntu 使用第一天
1. 在 apt-get install xxx 时候 未选择合适的源 -> 改 sources.list2. 接1 未配好 DNS -> http://dudns.baidu.com/u ...
- 非阻塞式线程安全列表-ConcurrentLinkedDeque
一.ConcurrentLinkedDeque public class ConcurrentLinkedDeque<E> extends AbstractCollection<E& ...
- 基于Kubernetes的WAF集群介绍
Kubernetes是Google开源的容器集群管理系统.它构建Docker技术之上,为容器化的应用提供资源调度.部署运行.服务发现.扩容缩容等整一套功能,可看作是基于容器技术的PaaS平台. 本文旨 ...
- C#之自定义特性
在前面介绍的代码中有使用特性,这些特性都是Microsoft定义好的,作为.NET Framework类库的一部分,许多特性都得到了C#编译器的支持. .NET Frmework也允许定义自己的特性. ...