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的Provider
public 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相关的操作会报ClosedSelectorException
public 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 AbstractSelector
extends Selector
{ private AtomicBoolean selectorOpen = new AtomicBoolean(true); // 是否打开 // The provider that created this selector
private final SelectorProvider provider; protected AbstractSelector(SelectorProvider provider) {
this.provider = provider;
}
// 三大key集合之一cancelledKeys
private final Set<SelectionKey> cancelledKeys = new HashSet<SelectionKey>(); void cancel(SelectionKey k) { // package-private
synchronized (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 SelectorImpl
extends AbstractSelector
{ // 已经准备IO的keys
protected Set<SelectionKey> selectedKeys; // 注册到该Selector的所有key
protected HashSet<SelectionKey> keys; // Public views of the key sets
private 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 selectedKeys
Set 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

  

NIO - Selector源码分析的更多相关文章

  1. 【Java】NIO中Selector的创建源码分析

    在使用Selector时首先需要通过静态方法open创建Selector对象 public static Selector open() throws IOException { return Sel ...

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

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

  3. Netty源码分析第2章(NioEventLoop)---->第5节: 优化selector

    Netty源码分析第二章: NioEventLoop   第五节: 优化selector 在剖析selector轮询之前, 我们先讲解一下selector的创建过程 回顾之前的小节, 在创建NioEv ...

  4. NIO 源码分析(05) Channel 源码分析

    目录 一.Channel 类图 二.begin 和 close 是什么 2.1 AbstractInterruptibleChannel 中的 begin 和 close 2.2 Selector 中 ...

  5. NIO 源码分析(04) 从 SelectorProvider 看 JDK SPI 机制

    目录 一.SelectorProvider SPI 二.SelectorProvider 加载过程 2.1 SelectorProvider 加载 2.2 Windows 下 DefaultSelec ...

  6. NIO 源码分析(03) 从 BIO 到 NIO

    目录 一.NIO 三大组件 Channels.Buffers.Selectors 1.1 Channel 和 Buffer 1.2 Selector 1.3 Linux IO 和 NIO 编程的区别 ...

  7. NIO 源码分析(01) NIO 最简用法

    目录 一.服务端 二.客户端 NIO 源码分析(01) NIO 最简用法 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) J ...

  8. jQuery原型属性constructor,selector,length,jquery和原型方法size,get,toArray源码分析

    首先看一下在jQuery1.7.1中定义的原型属性和方法有哪些? init方法作为实际的构造函数已经详细分析过了,需要了解可以参考http://www.cnblogs.com/yy-hh/p/4492 ...

  9. Netty源码分析第3章(客户端接入流程)---->第4节: NioSocketChannel注册到selector

    Netty源码分析第三章: 客户端接入流程 第四节: NioSocketChannel注册到selector 我们回到最初的NioMessageUnsafe的read()方法: public void ...

随机推荐

  1. Inno Setup入门(二十)——Inno Setup类参考(6)

    http://379910987.blog.163.com/blog/static/3352379720112515819485/ 存储框 存储框也是典型的窗口可视化组件,同编辑框类似,可以输入.显示 ...

  2. 基于Nginx的开墙方案

    Kubernetes集群内部通过服务名能进行相互调用,但如果Kubernetes中的pod需要调用外部服务,而且这些外部服务是属于不同的安全区域,就面临开墙的问题,因为Kubernetes Pod能够 ...

  3. [转]Insert, Update, and Delete Destination table with SSIS

    本文转自:http://www.rad.pasfu.com/index.php?/archives/150-Insert,-Update,-and-Delete-Destination-table-w ...

  4. Android应用内使用新浪微博SDK发送微博(不调用微博客户端)

    需求 手头的一个应用需要添加分享到新浪微博的功能,这个功能在现在的应用上是非常的普遍的了. 分享到新浪微博,其实就是发送一条特定内容的微博,所以需要用到新浪微博SDK了. 微博SDK SDK的下载地址 ...

  5. 【架构】Twitter高性能RPC框架Finagle介绍

    Twitter的RPC框架Finagle简介 Finagle是Twitter基于Netty开发的支持容错的.协议无关的RPC框架,该框架支撑了Twitter的核心服务.来自Twitter的软件工程师J ...

  6. C# WinForm 异步执行耗时操作并将过程显示在界面中

    private void button3_Click(object sender, EventArgs e)        {            RunAsync(() =>         ...

  7. 工程web-inf 下文件,路径访问

    直接用相对路径../即可 效果:

  8. SOA服务总线设计

    背景 基于总线的设计,借鉴了计算机内部硬件组成的设计思想(通过总线传输数据).在分布式系统中,不同子系统之间需要实现相互通信和远程调用,比较直接的方式就是“点对点”的通信方式,但是这样会暴露出一些很明 ...

  9. 内网渗透技巧:判断机器真实外网IP的5种方法总结

    在内网渗透中有时需要在某台WEB服务器中留下后门,该机器可以通过内网IP建立IPC连接,但还需要获知外网IP或域名才能访问Wbshell,在无网关权限的情况下,我总结了有如下方法: 1.通过nsloo ...

  10. python中,== 与 is 之间区别

    在python中,== 与 is 之间既有区别,又有联系,本文将通过实际代码的演示,力争能够帮助读到这篇文章的朋友以最短的时间理清二者的关系,并深刻理解它们在内存中的实现机制. 扯淡的话不多说,下面马 ...