1. 背景

SelectableChannel对象的多路复用器。

可以通过调用Selector.open()方法创建Selector对象。Selector.open()方法会利用系统默认的SelectorProvider创建Selector对象。也可以通过自定义SelectorProvider对象的openSelector方法创建Selector。Selector会一直处于打开状态,直到调用它的close方法。

SelectionKey对象表示每一个注册到Selector的SelectorChannel。每个Selector维护3个集合:

  • key集合:表示注册到该Selector的Channel,通过Channel的register方法可以往key集合中添加元素,取消的key在selection操作之间从key集合中移除,key集合不能直接修改,通过keys方法返回。
  • selected-key集合:在selection操作中,从所有的key集合中识别出已经ready的key对应的Channel。通过执行set的remove可以移除key或者执行迭代器对象的remove。否则key将不能通过其他方式移除。不可以直接增加到selected-key集合中。
  • cancelled-key集合:key已经被取消,但是对应的Channel还没有撤销,这个集合不可以直接访问,这个cancelled-key总是key集合的子集。当key被取消,close对应的Channel或执行它的cancel方法,则添加key到cancelled-key集合中。取消key将导致下一次Selection操作时它的通道被撤销,同时将从所有的Selector的key集合中删除。

备注:新创建的Selector对象,这3个集合都是空集合。

Selection

在每次执行Selection操作时,key可能从selected-key集合中增加或删除,也可能从key集合和cancelled-key集合中删除。通过select(),select(long),selectNow()方法执行Selection操作,包含3个步骤:

(1)

2. Selector源码分析

2.1 API

public abstract class Selector implements Closeable {protected Selector() { }// 创建Selector对象public static Selector open() throws IOException {return SelectorProvider.provider().openSelector();}// 检测Selector是否打开public abstract boolean isOpen();// 返回创建该Selector的Providerpublic abstract SelectorProvider provider();// 返回Key集合,key集合不能被直接修改,只有在被cancel和channel被撤销的时候key才被移除。并且不是线程安全的集合。public abstract Set<SelectionKey> keys();// 返回selected-key集合,key可以直接移除,但是不可以直接增加。并且不是线程安全的集合。public abstract Set<SelectionKey> selectedKeys();// 选择channel有IO事件的key。// 该方法是非阻塞的selection操作,如果自上次selection操作之后无channel具有IO事件,该方法会立刻返回零。// 执行该方法会立刻清除之前执行的wakeup影响。public abstract int selectNow() throws IOException;// 阻塞操作,只有在以下的状态变化时://(1)至少有一个IO的channel(2)调用selector.wakeup方法(3)当前线程被interrupt(4)timeout时间到(毫秒)public abstract int select(long timeout)throws IOException;// 阻塞操作,返回条件与select(long timeout)类似public abstract int select() throws IOException;// 唤醒当前select阻塞的操作:如果另一个线程当前阻塞在select或select(long)方法。// 如果当前没有select阻塞,则下次执行select或select(long)则直接返回,除非selectNow同时执行;//之后select和select(long)方法会正常阻塞;// 如果在select操作之间多次调用wakeup与调用一次效果是一样的public abstract Selector wakeup();// 关闭Selector。// 调用close方法,如果当前阻塞在selection操作,就像调用wakeup方法一样会立刻中断操作// 与该selector关联的未cancelled的key将失效,它们的channel将撤销,与Selector相关的其他资源将释放。// 如果Selector已经关闭,执行这个方法将没有影响。// selector关闭之后,如果执行与selector相关的操作会报ClosedSelectorExceptionpublic abstract void close() throws IOException;}

2.2 类图

2.3 AbstractSelector

AbstractSelector主要实现了Selector的打开关闭的状态维护,支持异步关闭和中断的begin和end方法,cancelledKeys等。

package java.nio.channels.spi;import java.io.IOException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.util.HashSet;
import java.util.Set;
import sun.nio.ch.Interruptible;
import java.util.concurrent.atomic.AtomicBoolean;// Selector的基本实现。
public abstract class AbstractSelectorextends Selector
{private AtomicBoolean selectorOpen = new AtomicBoolean(true); // 是否打开// The provider that created this selectorprivate final SelectorProvider provider;protected AbstractSelector(SelectorProvider provider) {this.provider = provider;}// 三大key集合之一cancelledKeysprivate final Set<SelectionKey> cancelledKeys = new HashSet<SelectionKey>();void cancel(SelectionKey k) {                       // package-privatesynchronized (cancelledKeys) {cancelledKeys.add(k);}}public final void close() throws IOException {boolean open = selectorOpen.getAndSet(false);if (!open)return;implCloseSelector();// 只有在Selector未关闭的情况下调用,并且只能被调用一次。}// 关闭Selector// 这个方法被close方法调用去执行Selector的关闭操作,只有在Selector未关闭的情况下调用,并且只能被调用一次。具体参考上面close实现protected abstract void implCloseSelector() throws IOException;public final boolean isOpen() {return selectorOpen.get();}public final SelectorProvider provider() {return provider;}protected final Set<SelectionKey> cancelledKeys() {return cancelledKeys;}// 为Selector注册Channel,这个方法被AbstractSelectableChannel.register方法调用protected abstract SelectionKey register(AbstractSelectableChannel ch,int ops, Object att);protected final void deregister(AbstractSelectionKey key) {((AbstractSelectableChannel)key.channel()).removeKey(key);}// -- Interruption machinery --private Interruptible interruptor = null;// 支持异步关闭和中断的begin和end方法protected final void begin() {if (interruptor == null) {interruptor = new Interruptible() {public void interrupt(Thread ignore) {AbstractSelector.this.wakeup();}};}AbstractInterruptibleChannel.blockedOn(interruptor);Thread me = Thread.currentThread();if (me.isInterrupted())interruptor.interrupt(me);}protected final void end() {AbstractInterruptibleChannel.blockedOn(null);}}  

备注:支持异步关闭和中断的机制,可以参考http://www.cnblogs.com/lujiango/p/8478154.html

2.4 SelectorImpl

package sun.nio.ch;import java.io.IOException;
import java.nio.channels.*;
import java.nio.channels.spi.*;
import java.net.SocketException;
import java.util.*;
import sun.misc.*;// Selector的基本实现
abstract class SelectorImplextends AbstractSelector
{// 已经准备IO的keysprotected Set<SelectionKey> selectedKeys;// 注册到该Selector的所有keyprotected HashSet<SelectionKey> keys;// Public views of the key setsprivate Set<SelectionKey> publicKeys;             // 不可变private Set<SelectionKey> publicSelectedKeys;     // 可删除,不可增加protected SelectorImpl(SelectorProvider sp) {super(sp);keys = new HashSet<SelectionKey>();selectedKeys = new HashSet<SelectionKey>();if (Util.atBugLevel("1.4")) {publicKeys = keys;publicSelectedKeys = selectedKeys;} else {publicKeys = Collections.unmodifiableSet(keys);publicSelectedKeys = Util.ungrowableSet(selectedKeys);}}public Set<SelectionKey> keys() {if (!isOpen() && !Util.atBugLevel("1.4"))throw new ClosedSelectorException();return publicKeys;}public Set<SelectionKey> selectedKeys() {if (!isOpen() && !Util.atBugLevel("1.4"))throw new ClosedSelectorException();return publicSelectedKeys;}// 对于Windows系统,需要WindowsSelectorImpl实现doSelect方法protected abstract int doSelect(long timeout) throws IOException; private int lockAndDoSelect(long timeout) throws IOException {synchronized (this) {if (!isOpen())throw new ClosedSelectorException();synchronized (publicKeys) {synchronized (publicSelectedKeys) {return doSelect(timeout);}}}}// select方法的实现public int select(long timeout)throws IOException{if (timeout < 0)throw new IllegalArgumentException("Negative timeout");return lockAndDoSelect((timeout == 0) ? -1 : timeout);}public int select() throws IOException {return select(0);}public int selectNow() throws IOException {return lockAndDoSelect(0);}public void implCloseSelector() throws IOException {wakeup();synchronized (this) {synchronized (publicKeys) {synchronized (publicSelectedKeys) {implClose();}}}}// 需要WindowsSelectorImpl实现protected abstract void implClose() throws IOException;void putEventOps(SelectionKeyImpl sk, int ops) { }protected final SelectionKey register(AbstractSelectableChannel ch,int ops,Object attachment){if (!(ch instanceof SelChImpl))throw new IllegalSelectorException();SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this);k.attach(attachment);synchronized (publicKeys) {implRegister(k);}k.interestOps(ops);return k;}protected abstract void implRegister(SelectionKeyImpl ski);void processDeregisterQueue() throws IOException {// Precondition: Synchronized on this, keys, and selectedKeysSet cks = cancelledKeys();synchronized (cks) {if (!cks.isEmpty()) {Iterator i = cks.iterator();while (i.hasNext()) {SelectionKeyImpl ski = (SelectionKeyImpl)i.next();try {implDereg(ski);} catch (SocketException se) {IOException ioe = new IOException("Error deregistering key");ioe.initCause(se);throw ioe;} finally {i.remove();}}}}}protected abstract void implDereg(SelectionKeyImpl ski) throws IOException;abstract public Selector wakeup(); // 定义唤醒方法}

  

2.5 WindowsSelectorImpl和EPollSelectorImpl

具体会分析windows平台的WindowsSelectorImpl,Linux平台的EPollSelectorImpl暂时不做分析。

3. WindowsSelectorImpl

  

转载于:https://www.cnblogs.com/lujiango/p/8458214.html

NIO - Selector源码分析相关推荐

  1. jquery Selector 源码分析

    /** * author:prk * date:2008-08-04 * comment:comment for selector of jQuery * */ var chars = jQuery. ...

  2. java channel源码_java nio ServerSocketChannel源码分析

    /** * 创建新实例 * * @param provider * The provider that created this channel */ protected ServerSocketCh ...

  3. 【Java】NIO中Selector的select方法源码分析

    该篇博客的有些内容和在之前介绍过了,在这里再次涉及到的就不详细说了,如果有不理解请看[Java]NIO中Channel的注册源码分析, [Java]NIO中Selector的创建源码分析 Select ...

  4. JDK源码分析 NIO实现

    总列表:http://hg.openjdk.java.net/ 小版本:http://hg.openjdk.java.net/jdk8u jdk:http://hg.openjdk.java.net/ ...

  5. netty源码分析系列——EventLoop

    2019独角兽企业重金招聘Python工程师标准>>> 前言 EventLoop也是netty作为一个事件驱动架构的网络框架的重要组成部分,netty主要通过它来实现异步编程,从前面 ...

  6. Netty源码分析第1章(Netty启动流程)----第4节: 注册多路复用

    Netty源码分析第1章(Netty启动流程)---->第4节: 注册多路复用 Netty源码分析第一章:Netty启动流程   第四节:注册多路复用 回顾下以上的小节, 我们知道了channe ...

  7. netty 5 alph1源码分析(服务端创建过程)

    研究了netty的服务端创建过程.至于netty的优势,可以参照网络其他文章.<Netty系列之Netty 服务端创建>是 李林锋撰写的netty源码分析的一篇好文,绝对是技术干货.但抛开 ...

  8. 创建线程的三种方法_Netty源码分析系列之NioEventLoop的创建与启动

    前言 前三篇文章分别分析了 Netty 服务端 channel 的初始化.注册以及绑定过程的源码,理论上这篇文章应该开始分析新连接接入过程的源码了,但是在看源码的过程中,发现有一个非常重要的组件:Ni ...

  9. Kafka 源码分析之网络层(二)

    上一篇介绍了概述和网络层模型实现<Kafka 源码分析之网络层(一)>,本编主要介绍在Processor中使用的nio selector的又一封装,负责具体数据的接收和发送. PS:丰富的 ...

最新文章

  1. codewars048: Triple Double
  2. Android:手把手教你 实现Activity 与 Fragment 相互通信(含Demo)
  3. shuoj 418 丢史蒂芬妮(素数筛+sg函数)
  4. 安卓学习-界面-ui-Toast
  5. 2017.3.24组合数学学习——容斥原理
  6. 求细胞数量pascal题解
  7. oracle 插入出错_使用sqlca打印错误原因,ORACLE-Proc:SQLCA
  8. Java预科篇1-学前
  9. 网页版进销存源码ERP多仓库管理系统源码
  10. bzoj 1066: [SCOI2007]蜥蜴(最大流)
  11. iOS开发-使用Storyboard进行界面跳转及传值
  12. Linux让Apache支持中文URL图片/文件名
  13. 计算机印屏幕功能无法使用,计算机电脑管家多屏协同功能无法使用
  14. 【深度好文】二维图像傅里叶变换 YYDS
  15. ESP8266自动下载电路分析
  16. Mysql 数据库迁移
  17. OAuth2及sa-token框架实践
  18. Python有多难?可以自学吗?
  19. Oracle服务与配置
  20. mysql如何锁表和解锁

热门文章

  1. New Linux2.6 I2C Driver Model Example
  2. testng的报告自定义笔记
  3. 《分布式系统:概念与设计》一1.3 分布式系统的趋势
  4. HBase+Phoenix整合入门--集群搭建
  5. ubuntu无线网络开关
  6. mysql left join 结果怎么这么慢
  7. java调度:(四) spring中使用quartz的配置文件.
  8. 轻量又高效,Apache Shiro 你值得拥有!
  9. SpringMVC实现简单登录
  10. ZkServer服务启动的逻辑-NIOServerCnxnFactory.configure