简介

Selector 是 java.nio.channels 包下的重要组件,阅读本文可以带你了解常用的 API。本文中把 Channel 翻译成信道,按照个人习惯也可以称作是通道、管道。

Selector 的核心组件有 SelectableChannel、Selector、SelectionKey。



多路复用选择器更多的是用在 Java 网络编程,网络编程的常用到就是 UDP / TCP 。SelectableChannel 的子类如下:

  • 抽象类 SelectableChannel,它可以通过 Selector 多路复用。要使用多路复用功能,首先要注册。SelectableChannel 通过 register(Selector, int) 方法注册到 Selector 中,并返回 SelectionKey 作为注册代表。
  • 抽象类 AbstractSelectableChannel ,它实现了 configureBlocking() 和 register() 方法,分别用于设置阻塞模式和注册事件
  • 抽象类 DatagramChannel 提供 UDP 信道服务。
  • 抽象类 ServerSocketChannel 提供 TCP 信道服务。主要用于服务器端
  • 抽象类 SocketChannel 代表连接服务端与客户端的信道。通过 ServerSocketChannel.accept() 获得。当服务端接收到一个来自客户端的连接时就会产生一个信道。

各信道支持的事件

通过 AbstractSelectableChannel#validOps() 方法,可以返回一个操作集合(类型是 int)。这个集合表示该信道支持的操作。int 的每一个 bit 位代表信道是否支持某类操作。

同一个 SelectableChannel 的完全实现类的 validOps 方法总是返回固定的值。

  • 目前 Selector 支持的事件类型是 4 种,分别是 SelectionKey.OP_ACCEPT、SelectionKey.OP_CONNECT、SelectionKey.OP_WRITE、SelectionKey.OP_READ,分别表示接受连接,主动连接,写,读
OP_ACCEPT OP_CONNECT OP_WRITE OP_READ
DatagramChannel 不支持 不支持 支持 支持
ServerSocketChannel 支持 不支持 不支持 不支持
SocketChannel 不支持 支持 支持 支持

注册事件及异常

AbstractSelectableChannel#register 方法可以注册指定事件到 Selector 中,并且还会保存注册成功后返回的 SelectionKey。

public final SelectionKey register(Selector sel, int ops, Object att)
{
synchronized (regLock) {
if (!isOpen())
throw new ClosedChannelException();
if ((ops & ~validOps()) != 0)
throw new IllegalArgumentException();
if (blocking)
throw new IllegalBlockingModeException();
// 从数组 SelectionKey[] 寻找选择器 sel 对应的 SelectionKey
SelectionKey k = findKey(sel);
if (k != null) {
k.interestOps(ops);
k.attach(att);
}
if (k == null) {
// 新注册一个 SelectionKey
synchronized (keyLock) {
if (!isOpen())
throw new ClosedChannelException();
// Selector 注册当前信道,并返回 SelectionKey
k = ((AbstractSelector)sel).register(this, ops, att);
// 将 SelectionKey 保存到数组中
addKey(k);
}
}
return k;
}
}

从这段源码中,我们还会留意到我们写代码常常会出现的几个异常:

  • 如果我们向 SelectableChannel 实例对象注册它不支持的事件,抛出 IllegalArgumentException
  • 我们注册事件前,没有调用 configureBlocking(false) 设置非阻塞,抛出 IllegalBlockingModeException

    以上这两个是首次运用 Selector 时最常碰到的异常,另外还有一些其他的异常,比如
  • 关闭 SelectorChannel 后,注册事件,抛出 ClosedChannelException
@Test(expected = ClosedChannelException.class)
public void ClosedChannelException() throws IOException {
DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(false);
channel.close();
channel.register(selector, SelectionKey.OP_WRITE);
}
  • 关闭 Selector 后,注册事件,抛出 ClosedSelectorException
@Test(expected = ClosedSelectorException.class)
public void ClosedSelectorException() throws IOException {
DatagramChannel channel = DatagramChannel.open();
channel.configureBlocking(false);
Selector selector = Selector.open();
selector.close();
channel.register(selector, SelectionKey.OP_READ);
}
  • 取消 SelectionKey 后,注册事件,抛出 CancelledKeyException
@Test(expected = CancelledKeyException.class)
public void CancelledKeyException() throws IOException {
DatagramChannel channel = DatagramChannel.open();
Selector selector = Selector.open();
channel.configureBlocking(false);
SelectionKey key = channel.register(selector, SelectionKey.OP_READ);
key.cancel();
// selector.selectNow(); // 如果刷新一下就不会抛出异常
channel.register(selector, SelectionKey.OP_WRITE);
}

通过 SelectableChannel#register 的注册方法,可以形成以下关系:

  • 先看标识“五角星”的 AbstractSelectableChannel,包含一个可扩容的 SelectionKey 数组,通过数组元素对应多个不同的 Selector
  • 再看标识“三角形”的 SelectionKey,通过这个对象,可以找到注册时对应的 selector 和 channel
  • 然后看标识“五角星”的 Selector,包含一个 HashSet<SelectionKey>,通过集合保存与多个 SelectableChannel 的对应关系
  • 总而言之,SelectableChannel 和 Selector 通过 SelectionKey 形成了多对多的关系

Selector 操作状态说明

下图是常见的 Selector 操作及其键集的变化示意图:

  • keys 键集,保存所有注册的 SelectionKey 的集合。
  • selectedKeys 就绪键集,保存所有准备就绪的键,通过 select 方法增加
  • cancelledKeys 取消键集,保存所有已取消的键,通过 select 方法,可以从 keys 中清除所有取消键,同时清空取消键集。

总结

Selector、SelectableChannel、SelectionKey 是 Java NIO 多路复用中的核心组件。

多路复用技术主要用于网络编程,SelectableChannel 其主要实现包含 DatagramChannel、ServerSocketChannel 和 SocketChannel。

  • Selecter 是 SelectableChannel 在设置非阻塞情况下使用的多路复用选择器。
  • SelectableChannel 支持注册事件的信道。
  • SelectionKey 是注册事件后,建立 Selector 与 SelectableChannel 之间关系的桥梁。

NIO入门之多路复用选择器Selector的更多相关文章

  1. NIO 选择器 Selector

    选择器提供选择执行已经就绪的任务的能力,这使得多元 I/O 成为可能.就像在第一章中描述的那样,就绪选择和多元执行使得单线程能够有效率地同时管理多个 I/O 通道(Channels).C/C++代码的 ...

  2. 第二章 NIO入门

    传统的同步阻塞式I/O编程 基于NIO的非阻塞编程 基于NIO2.0的异步非阻塞(AIO)编程 为什么要使用NIO编程 为什么选择Netty 第二章 NIO 入门 2.1 传统的BIO编程 2.1.1 ...

  3. NIO 入门(转)

    NIO 入门 Greg Travis2003 年 11 月 17 日发布 分享此页面 WeiboGoogle+用电子邮件发送本页面 20 在开始之前 关于本教程 新的输入/输出 (NIO) 库是在 J ...

  4. NIO入门-----01

    package com.sico.pck01_nio; import java.nio.ByteBuffer; import org.junit.Test; /**  * @author Sico   ...

  5. 史上最强Java NIO入门:担心从入门到放弃的,请读这篇!

    本文原题“<NIO 入门>,作者为“Gregory M. Travis”,他是<JDK 1.4 Tutorial>等书籍的作者. 1.引言 Java NIO是Java 1.4版 ...

  6. NIO入门

    NIO:Non-blocking IO,即非阻塞式IO. 标准的IO基于字节流和字符流进行操作. 而NIO基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从Channel读取到Bu ...

  7. Java NIO入门(二):缓冲区内部细节

    Java NIO 入门(二)缓冲区内部细节 概述 本文将介绍 NIO 中两个重要的缓冲区组件:状态变量和访问方法 (accessor). 状态变量是前一文中提到的"内部统计机制"的 ...

  8. Java NIO入门

    NIO入门 前段时间在公司里处理一些大的数据,并对其进行分词.提取关键字等.虽说任务基本完成了(效果也不是特别好),对于Java还没入门的我来说前前后后花了2周的时间,我自己也是醉了.当然也有涉及到机 ...

  9. Flume-NG源码阅读之SourceRunner,及选择器selector和拦截器interceptor的执行

    在AbstractConfigurationProvider类中loadSources方法会将所有的source进行封装成SourceRunner放到了Map<String, SourceRun ...

随机推荐

  1. SpringMVC 学习笔记(三)数据的校验

    34. 尚硅谷_佟刚_SpringMVC_数据绑定流程分析.avi 例如:在jsp中输入一个String字符串类型,需要转换成Date类型的流程如下 convertservice对传入的数据进行转换 ...

  2. mysql 出现You can't specify target table for update in FROM clause错误的解决方法

    mysql出现You can’t specify target table for update in FROM clause 这个错误的意思是不能在同一个sql语句中,先select同一个表的某些值 ...

  3. Executors框架之ScheduledExecutorService实现定时任务

    一.简介 An ExecutorService that can schedule commands to run after a given delay, or to execute periodi ...

  4. org.hibernate.LazyInitializationException异常解决办法

    org.hibernate.LazyInitializationException异常failed to lazily initialize a collection...的解决方案使用hiberna ...

  5. JavaScript基础Literal 与 Constructor(008)

    JavaScript支持以字面声名法(Literal)的方式来声名对象和数组,相对于构造函数(constructor)的方式,Literal的声 名方式更简洁,更易读,也更少导致Bug.事实上,JSO ...

  6. 《JavaScript高级程序设计》(第二版)

    这本书的作者是 Nicholas C.Zakas ,博客地址是 http://www.nczonline.net/ ,大家可以去多关注,雅虎的前端工程师,是YUI的代码贡献者,可想而知这本书得含金量, ...

  7. 二.4vue展示用户数据及用户组操作以及给用户组添加额外字段

    一.用户列表 1.新建(1)views/users/index.vue: <template> <div class="user-list-container"& ...

  8. Nmap常见扫描方式流量分析

    环境说明 扫描者:manjaro linux , IP地址:192.168.31.160 被扫描者:centos 7,IP地址:192.168.31.175 分析工具:wireshark nmap 版 ...

  9. REST,RPC和GraphQL应用场景,WebHooks、WebSocket、HTTP Streaming应用场景。

    一.请求--响应API. 请求--响应类的API的典型做法是,通过基于HTTP的Web服务器暴露一个/套接口.API定义一些端点,客户端发送数据的请求到这些端点,Web服务器处理这些请求,然后返回响应 ...

  10. 【转】Hbuilder配置Avalon、Vue指令提示

    转载自CSDN http://blog.csdn.net/jianggujin/article/details/71419828 我本人是一名Java后端开发,偶尔也会研究一下前端内容,因为Hbuil ...