NIO(四、Selector)
目录
NIO(一、概述)
NIO(二、Buffer)
NIO(三、Channel)
NIO(四、Selector)
Selector
前面两个章节都描述了Buffer和Channel,那这个章节就描述NIO三个最核心部分的最后一块内容 - 选择器(Selector)
如何使用
在前面的章节中描述过多路复用,一个线程通过选择器处理和管理多个通道。由此可见,选择器是用来处理多个通道并监听其通道事件的组件。
- Create
只需要调用 open() 即可创建一个Selector对象:
Selector selector = Selector.open();
- Register
通过 register() 方法注册通道:
ServerSocketChannel channel = ServerSocketChannel.open();
channel.configureBlocking(false);
SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_ACCEPT);
在注册通道之前,把通道设置成非阻塞模式,观察源码会发现 register() 会校验当前通道是否为非阻塞模式,当是阻塞模式时,会抛出IllegalBlockingModeException 异常。在前面一个章节也提过,为什么FileChannel没有继承SelectableChannel,因为它不需要多路复用,所以在使用通道的时候,只有FileChannel不能向选择器注册通道,凡是继承SelectableChannel都能够向选择器注册通道。
注册通道方法的第二个参数是SelectionKey中定义的操作类型,你可以填入任何你感兴趣的操作类型,只要这个通道支持,同样,在执行 register() 方法时也会校验该通道是否能够支持该操作。
注册方法同样也会返回一个SelectionKey对象。
- Attach Object
注册通道的 register() 方法有一个重载方法,可以向选择器注册通道的时候,选择想要带上的附加对象:
public abstract SelectionKey register(Selector sel, int ops, Object att)
throws ClosedChannelException;
例如,使用时附加上一个字符串:
String ch_name = "123";
SelectionKey selectionKey = channel.register(selector, SelectionKey.OP_ACCEPT,ch_name);
获取这个字符串可通过 attachment() 来获取:
// 接收数据
String ch_name_accept = (String) selectionKey.attachment();
当然,注册时返回的SelectionKey对象也可以在使用时候附加你想要的附加对象:
selectionKey.attach(ch_name);
- Block
因为是一个线程通过选择器来操作通道,那么选择器在操作通道时,必定在处理一个通道的时候,另一个事件已就绪的通道处于等待状态。在确定一个通道事件就绪之后,才能去操作这个通道。上文中讲到使用注册方法register使用的代码示例,将ServerSocketChannel对象向选择器注册,同时关注了这个通道的OP_ACCEPT操作类型事件,那么我们什么时候能确定该通道的accept事件就绪,可以操作这个通道了。选择器为我们提供了三个重载的 select() 方法,这三个方法的主要功能就是选择是否阻塞直到该选择器中的通道所关注的事件就绪,来让程序继续往下运行。
首先看 select() 方法,该方法会一直阻塞下去,直到选择器中的通道关注的事件就绪:
selector.select();
参数5000是5秒,参数以毫秒为单位。这个方法会一直阻塞5秒,5秒之内如果没有通道事件就绪的话程序会往下运行:
selector.select(5000);
selectNow()其实就是非阻塞,无论有无通道事件就绪,程序都会向下执行:
selector.selectNow();
这三个方法的本质区别无非是选择器阻塞或者等待一个或多个通道的事件就绪有多长时间。
- Keys & SelectionKeys
我们每为一个通道执行 register() 注册方法,就会返回一个SelectionKey,那么这个选择器所有已就绪的SelectionKey就是通过selectedKeys()来获取:
Set<SelectionKey> selectionKeys = selector.selectedKeys();
一般这个方法是在select() 之后执行,因为到这一步就意味着要通过这个轮询每个就绪的通道。
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isAcceptable()) {
// 执行通道的操作
}
//执行完成移除
iterator.remove();
}
到这里说的是已就绪的通道,那么所有的 SelectionKey 集可以通过 keys() 方法获取:
Set<SelectionKey> keys = selector.keys();
- wake up
在使用Selector对象的 select() 或者 select(long) 方法时候,当前线程很可能一直阻塞下去,那么用另一个线程去执行 Selector.wakeUp() 方法会唤醒当前被阻塞的线程,使其 select() 立即返回。
当然,如果当前线程没有阻塞,那么执行了wakeUp() 方法之后,下一个线程的 select() 方法会被立即返回,不再被阻塞下去。 - close
显然,close() 方法能够关闭当前的选择器。
当一个线程当前呈阻塞状态,那么中止这种状态需要执行选择器的 wakeUp() 方法,close()方法的实现正是这么做的,先唤醒被阻塞的线程,然后继续接下来的操作。接下来就会会置空所有的通道、所有就绪的SelectionKey,让这个选择器上的轮询组件也闲置下来。
SelectionKey
SelectionKey的功能类似于通道的一个注册令牌。
这个类定义了4个操作类型,每种操作类型都对应了相应的事件,通过监听这几种不同的事件,在触发该事件时表示所对应的操作已准备就绪:
| 操作类型 | 值 | 描述 |
|---|---|---|
| OP_READ | 1 << 0 | 读操作 |
| OP_WRITE | 1 << 2 | 写操作 |
| OP_CONNECT | 1 << 3 | 连接socket操作 |
| OP_ACCEPT | 1 << 4 | 接受socket操作 |
这里得提一句,所有继承SelectableChannel的通道都会定义自己能够支持的操作类型,可以通过具体通道的 validOps() 方法查看,例如SocketChannel支持read、write、connect这几种操作:
// SocketChannel类
public final int validOps() {
return (SelectionKey.OP_READ | SelectionKey.OP_WRITE | SelectionKey.OP_CONNECT);
}
- interestOps & readyOps
这是SelectionKey的实现类中定义的变量:
private volatile int interestOps;
private int readyOps;
interestOps用来存储感兴趣的操作集,readyOps用来存储已经就绪的操作集。
其中 interestOps() 方法和 nioInterestOps() 都会返回interestOps,不同的是interestOps()会校验是否已执行 cancel() ,如果已经取消则会抛出 CancelledKeyException 异常。readyOps同样也有 readyOps() 和 nioReadyOps() 方法,逻辑与interestOps几乎一致。
观察这段SelectionKey抽象类已经实现的代码:
// SelectionKey 类
public final boolean isAcceptable() {
return (readyOps() & OP_ACCEPT) != 0;
}
当判断是否访问就绪的时候,只要 readyOps() 与相应的操作类型相与,非零就返回true,代表接受请求操作已就绪。这个是SelectionKey已提供的方法,但是SelectionKey并未提供同样返回boolean判断某个操作在interestOps集是否存在,我们可以自己实现这些方法:
private boolean isInterestRead(SelectionKey selectionKey){
return (selectionKey.interestOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_READ;
}
private boolean isInterestWrite(SelectionKey selectionKey){
return (selectionKey.interestOps() & SelectionKey.OP_WRITE) == SelectionKey.OP_WRITE;
}
private boolean isInterestConnect(SelectionKey selectionKey){
return (selectionKey.interestOps() & SelectionKey.OP_CONNECT) == SelectionKey.OP_CONNECT;
}
private boolean isInterestAccept(SelectionKey selectionKey){
return (selectionKey.interestOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT;
}
- Channel、Selector
在SelectionKey中获取通道或者选择器只需要调用其中的两个方法即可:
SelectableChannel selectableChannel = selectionKey.channel();
Selector sel = selectionKey.selector();
NIO(四、Selector)的更多相关文章
- java学习-NIO(四)Selector
这一节我们将探索选择器(selectors).选择器提供选择执行已经就绪的任务的能力,这使得多元 I/O 成为可能.就像在第一章中描述的那样,就绪选择和多元执行使得单线程能够有效率地同时管理多个 I/ ...
- Java NIO类库Selector机制解析(上)
一. 前言 自从J2SE 1.4版本以来,JDK发布了全新的I/O类库,简称NIO,其不但引入了全新的高效的I/O机制,同时,也引入了多路复用的异步模式.NIO的包中主要包含了这样几种抽象数据类型: ...
- NIO组件Selector工作机制详解(上)
转自:http://blog.csdn.net/haoel/article/details/2224055 一. 前言 自从J2SE 1.4版本以来,JDK发布了全新的I/O类库,简称NIO,其不但 ...
- Java NIO类库Selector机制解析--转
一. 前言 自从J2SE 1.4版本以来,JDK发布了全新的I/O类库,简称NIO,其不但引入了全新的高效的I/O机制,同时,也引入了多路复用的异步模式.NIO的包中主要包含了这样几种抽象数据类型: ...
- Java NIO之Selector(选择器)
历史回顾: Java NIO 概览 Java NIO 之 Buffer(缓冲区) Java NIO 之 Channel(通道) 其他高赞文章: 面试中关于Redis的问题看这篇就够了 一文轻松搞懂re ...
- Nio使用Selector客户端与服务器的通信
使用NIO的一个最大优势就是客户端于服务器自己的不再是阻塞式的,也就意味着服务器无需通过为每个客户端的链接而开启一个线程.而是通过一个叫Selector的轮循器来不断的检测那个Channel有消息处理 ...
- Java NIO类库Selector机制解析(下)
五. 迷惑不解 : 为什么要自己消耗资源? 令人不解的是为什么我们的Java的New I/O要设计成这个样子?如果说老的I/O不能多路复用,如下图所示,要开N多的线程去挨个侦听每一个Channel ...
- NIO的Selector
参考自 Java NIO系列教程(六) Selector Java-NIO-Selector java.nio.channels.Selector NIO新功能Top 10(下) 出发点: 如何管理多 ...
- Java NIO 选择器(Selector)的内部实现(poll epoll)
http://blog.csdn.net/hsuxu/article/details/9876983 之前强调这么多关于linux内核的poll及epoll,无非是想让大家先有个认识: Java NI ...
- NIO组件Selector调用实例
*对于nio的非阻塞I/O操作,使用Selector获取哪些I/O准备就绪,注册的SelectionKey集合记录关联的Channel这些信息.SelectionKey记录Channel对buffer ...
随机推荐
- 关于bootstrap 在MVC里 模态框里加载iframe页面做编辑的时候
前台代码 <div class="modal fade" id="myModal" tabindex="-1" role=" ...
- SQL中如何使用EXISTS替代IN
原创作品,可以转载,但是请标注出处地址http://www.cnblogs.com/V1haoge/p/6385312.html 我们在程序中一般在做SQL优化的时候讲究使用EXISTS带替代IN的做 ...
- 解决 Eclipse build workspace validation javascript 慢的问题
参考: http://blog.csdn.net/zhangzikui/article/details/24805935 http://www.cnblogs.com/wql025/p/4978351 ...
- 使用fliter实现ie下css中rgba的效果
直接举例吧background-color: rgba(255,255,255,0.8); 设置背景颜色为白色,不透明度设为80% 在ie下可以用fliter这一属性进行设置: filter:prog ...
- Java Web(二) Servlet中response、request乱码问题解决
三月不减肥,五月徒伤悲,这就是我现在的状态,哈哈~ 健身.博客坚持. --WH 一.request请求参数出现的乱码问题 get请求: get请求的参数是在url后面提交过来的,也就是在请求行中, M ...
- c++ STL常用算法使用方法
#include <string> #include <vector> #include <functional> #include <iostream> ...
- JS分两种数据类型,你都知道吗?
大牛请无视此篇! JS主要分基本数据类型和引用数据类型,这两者区别可大了,此篇看完必有长进,下面进入正题 首先我们看下什么是基本数据类型(概念我就不说了,直接上代码): var i = 10: var ...
- Spring AOP的解读
一.为什么会有AOP 在日常的开发中经常会有这样一种场景,支付相关的业务中经常需要记录日志,而记录的日志大体相同:这样就会造成支付组件和日志组件强关联,耦合在一起了.而AOP的出现就是为了解决这种问题 ...
- UE4从零搭建CF游戏关卡(蓝图篇)
一 往关卡中放置物体 二 Static Mesh 和 BSP 从今天开始我们要建立一个类似CF游戏的场景,不写一句代码,纯蓝图实现. 三 英语学习 今天早上开会,经理说了一件让我震惊的事情,她说最近大 ...
- Boot Sector - Hello world
1. code bits org 7c00h mov ax, cs mov ds, ax mov es, ax call DispStr jmp $ DispStr: mov ax, BootMess ...