Java NIO5:选择器1---理论篇

选择器

最后,我们探索一下选择器。由于选择器内容比较多,所以本篇先偏理论地讲一下,后一篇讲代码,文章也没有什么概括、总结的,写到哪儿算哪儿了,只求能将选择器写明白,并且将一些相对重要的内容加粗标红。

选择器提供选择执行已经就绪的任务的能力,这使得多元I/O成为了可能,就绪执行和多元选择使得单线程能够有效地同时管理多个I/O通道。

某种程序上来说,理解选择器比理解缓冲区和通道类更困难一些和复杂一些,因为涉及了三个主要的类,它们都会同时参与到这整个过程中,这里先将选择器的执行分解为几条细节:

1、创建一个或者多个可选择的通道

2、将这些创建的通道注册到选择器对象中

3、选择键会记住开发者关心的通道,它们也会追踪对应的通道是否已经就绪

4、开发者调用一个选择器对象的select()方法时,相关的键会被更新,用来检查所有被注册到该选择器的通道

5、获取一个键的集合,从而找到当时已经就绪的通道,通过遍历这些键,开发者可以选择出每个从上次调用select()开始直到现在已经就绪的通道

对于选择器的理解,大致就是这么几步,OK,接下去再进一步,看一下和选择器相关的三个类。

选择器、可选择通道和选择键类

选择器(Selector)

选择器类管理着一个被注册的通道集合的信息和它们的就绪状态。通道是和选择器一起被注册的,并且使用选择器来更新通道的就绪状态,当这么做的时候,可以选择将被激发的线程挂起直到有就绪的通道。

可选择通道(SelectableChannel)

这个抽象类提供了实现通道的可选择性所需要的公共方法,它是所有支持就绪检查的通道类的父类,FileChannel对象不是可选择的,因为它们没有继承SelectableChannel,所有Socket通道都是可选择的,包括从管道(Pipe)对象中获得的通道。SelectableChannel可以被注册到Selector对象上,同时可以设定对哪个选择器而言哪种操作是感兴趣的。一个通道可以被注册到多个选择器上,但对每个选择器而言只能被注册一次。

选择键(SelectionKey)

选择键封装了特定的通道与特定的选择器的注册关系。选择键对象呗SelectableChannel.register()返回并提供一个表示这种注册关系的标记。选择键包含了两个比特集(以整数形式进行编码),指示了该注册关系所关心的通道操作,以及通道已经准备好的操作。

用一张UML图来描述一下选择器、可选择通道和选择键:

建立选择器

前面讲了,选择器的作用是管理了被注册的通道集合和它们的就绪状态,假设我们有三个Socket通道的选择器,可能会有类似的代码:

...
Selector selector = Selector.open();
channel1.register(selector, SelectionKey.OP_READ); channel2.register(selector, SelectionKey.OP_WRITE); channel3.register(selector, SelectionKey.OP_READ | OP_WRITE); channel4.register(selector, SelectionKey.OP_READ | OP_ACCEPT); ready = selector.select(10000); ...

这种操作用图表示就是:

代码创建了一个新的选择器,然后将这四个(已经存在的)Socket通道注册到选择器上,而且感兴趣的操作各不相同。select()方法在将线程置于睡眠状态直到这些感兴趣的事件中的一个发生或者10秒钟过去,这就是所谓的事件驱动

再稍微看一下Selector的API细节:

public abstract class Selector
{
    ...
    public static Selector open() throws IOException;
    public abstract boolean isOpen();
    public abstract void close() throws IOException;
    public abstract SelectionProvider provider();
    ...
}

Selector是通过调用静态工厂方法open()来实例化的,这个从前面的代码里面也看到了,选择器不是像通道或流那样的基本I/O对象----数据从来没有通过他们进行传递

通道是调用register方法注册到选择器上的,从代码里面可以看到register()方法接受一个Selector对象作为参数,以及一个名为ops的整数型参数,第二个参数表示关心的通道操作。在JDK1.4中,有四种被定义的可选择操作:读(read)、写(write)、连接(connect)和接受(accept)。

注意并非所有的操作都在所有的可选择通道上被支持,例如SocketChannel就不支持accept。

使用选择键

接下来看看选择键,选择键的API大致如下:

public abstract class SelectionKey
{
    public static final int OP_READ;
    public static final int OP_WRITE;
    public static final int OP_CONNECT;
    public static final int OP_ACCEPT;
    public abstract SelectableChannel channel();
    public abstract Selector selector();
    public abstract void cancel();
    public abstract boolean isValid();
    public abstract int interestOps();
    public abstract void iterestOps(int ops);
    public abstract int readyOps();
    public final boolean isReadable();
    public final boolean isWritable();
    public final boolean isConnectable();
    public final boolean isAcceptable();
    public final Object attach(Object ob);
    public final Object attachment();
}

就像前面提到的,一个键表示了一个特定的通道对象和一个特定的选择器对象之间的注册关系,channel()方法和selector()方法反映了这种关系。

开发者可以使用cancel()方法终结这种关系,可以使用isValid()方法来检查这种有效的关系是否仍然存在,可以使用readyOps()方法来获取相关的通道已经就绪的操作。不过我们往往不需要使用readyOps()方法,SelectionKey类定义了四个便于使用的布尔方法来为开发者测试通道的就绪状态:

if (key.isWritable())

这种写法就等价于:

if ((key.readyOps() & SelectionKeys.OPWRITE) != 0)

isWritable()、isReadable()、isConnectable()、isAcceptable()四个方法在任意一个SelectionKey对象上都能安全地调用。

时间: 2024-12-10 06:20:57

Java NIO5:选择器1---理论篇的相关文章

【SSH2(理论篇)】--Struts2配置详解

上篇博客讨论了SSH2框架模型,在开发过程中发现SSH2的开发模型其实类似于经典的三层模式,在每一层中分别添加了不同的框架,显示层使用的是Struts2进行配置的,业务逻辑层使用的是Spring配置,数据持久层则采用的是Hibernate,开发模式简单易懂,接下来将会分别从三层着手讨论每一层的运行内容. 一.Struts体系简介 struts,是Apache软件基金会(ASF)赞助的一个开源项目,它通过采用Java Servlet/JSP技术,实现了基于Java EE Web应用的Model-V

Oracle内存组件理论篇一

目标 1.SGA结构 2.PGA结构 1.SGA Shared pool 1).共享池是对SQL.PL/SQL程序进行语法分析.编译.执行的内存区域. 在执行SELECT * FROM emp语句时,会对sql语句进行语法分析->编译->生成执行计划->运行执行计划等,这些操作都在共享池中完成: 如果再次执行SELECT * FROM emp语句时,会在共享池中查找是否有相同的sql,如果存在则省去编译.生成执行计划操作步骤而是直接运行执行计划. 因此养成良好的编码习惯对于提高oracl

Maven进价之理论篇

Maven:基于Java平台的项目构建.依赖管理和项目信息管理. 1.构建 Maven标准化了构建过程 构建过程:编译.运行单元测试.生成文档.打包和部署 避免重复:设计.编码.文档.构建 2.依赖管理 提供了中央仓库,帮助我们自动下载构件 通过坐标系统准确定位每一个构件(artifact),通过一组坐标可以找到任何一个java类库(如jar文件) 3.项目信息管理 直接的项目信息:项目描述.开发者列表.版本控制系统地址.许可证... 更多有价值信息:项目文档.测试报告.静态分析报告.源码版本日

java复习汇总之面试篇

这些个人感觉都是基础,希望看的园友不要喷. 1.什么是servlet线程安全,如何解决? 2.spring事物管理,在项目中你是怎么管理事物的? 3.java中的有几种线程? 4.java有几种锁? 5.怎么理解java中的多态,项目中何时用到抽象类? 6.如何处理js,sql注入? 7.struts2拦截器的原理? 8.如何理解spring AOP? 11.java中常用的集合,在项目中用到时,有什么需要注意的? 12.如果优化项目中sql,为什么要那样优化? 13.java性能优化? 14.

分布式系统(3)---Web Service实战--CXF理论篇

第一篇:CXF理论篇 在Java领域,WebService的框架很多,例如:AXIS,XFire,CXF等.AXIS,XFire相对比较成熟. Axis全程Apache Extensible Interaction System即Apache可扩展交互系统.是第三代Apache SOAP.本质上就是一个SOAP引擎,但不完全是一个SOAP引擎,它还是一个独立的SOAP服务器和一个嵌入Servlet引擎的服务器. XFire是新一代的Java Web服务引擎,可以非常容易地和Spring集成.是c

《JVM调优实战-理论篇》

1 理论篇1.1 多功能养鱼塘-JVM内存大鱼塘O(可分配内存): JVM可以调度使用的总的内存数,这个数量受操作系统进程寻址范围.系统虚拟内存总数.系统物理内存总数.其他系统运行所占用的内存资源等因素的制约.小池塘A(堆内存):JVM运行时数据区域,它为类实例和数组分配的内存.堆可以是固定大小的也可以是可变大小的.其中 Heap = {Old + NEW = { Eden , from, to } }.小池塘B(非堆内存):包括所有线程之间共享的一个方法区域和JVM为优化或内部处理所分配的内存

如何编写高质量的 JS 函数(3) --函数式编程[理论篇]

本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/EWSqZuujHIRyx8Eb2SSidQ作者:杨昆 [编写高质量函数系列]中, <如何编写高质量的 JS 函数(1) -- 敲山震虎篇>介绍了函数的执行机制,此篇将会从函数的命名.注释和鲁棒性方面,阐述如何通过 JavaScript 编写高质量的函数. <如何编写高质量的 JS 函数(2)-- 命名/注释/鲁棒篇>从函数的命名.注释和鲁棒性方面,阐述如何通过 JavaScri

面向对象函数基本知识(二)理论篇

类是对象的抽象,对象时类的实例(实例化) 类是具有相同属性的行为和的一组对象的抽象 在软件系统中我们用类来模拟现实生活中的实体 java可以写多个类,但是公共类只能有一个,且公共类必须与类名保持一致 java文件和class文件的关系 1.1篇java文件可以书写多个类 2.1篇java文件只能有一个公共类,公共类类名必须和java文件吗一致 --实际书写过程中做到一篇java文件只写一个类(内部类除外) 3.1篇java文件可以生成多篇class文件·1,class文件数量依赖于类的数量(一个

Java集合类汇总记录--guava篇

BiMap HashBiMap<K,V> 实现了两份哈希表数据结构(本类独立实现),分别负责两个方向的映射. EnumBiMap<K,V> 两个EnumMap对象分别负责两个方向的映射. EnumHashBiMap<K,V> 一个EnumMap对象负责K到V映射,一个HashMap对象负责V到K的影身. MultiMap HashMultimap<K,V> 使用HashMap<K,HashSet<V>>对象实现. TreeMultim

【机器学习】Logistic Regression 的前世今生(理论篇)

Logistic Regression 的前世今生(理论篇) 本博客仅为作者记录笔记之用,不免有非常多细节不正确之处. 还望各位看官能够见谅,欢迎批评指正. 博客虽水,然亦博主之苦劳也. 如需转载,请附上本文链接,不甚感激! http://blog.csdn.net/cyh_24/article/details/50359055 写这篇博客的动力是源于看到了以下这篇微博: 我在看到这篇微博的时候大为触动,由于,如果是rickjin来面试我.我想我会死的非常慘,由于他问的问题我基本都回答不上来.