系列文章目录
文章目录
- 系列文章目录
- 前言
前言
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。
Selector提供选择执行已经就绪的任务的能力,使得多元 I/O 成为可能,就绪选择和多元执行使得单线程能够有效率地同时管理多个 I/O channel。
C/C++许多年前就已经有 select()和 poll()这两个POSIX(可移植性操作系统接口)系统调用可供使用。许多os也提供相似的功能,但对Java 程序员来说,就绪选择功能直到 JDK 1.4 才成为可行方案。
Selector的创建过程如下:
// 1.创建Selector
Selector selector = Selector.open();
// 2.将Channel注册到选择器中
// ....... new channel的过程 ....
//Notes:channel要注册到Selector上就必须是非阻塞的,所以FileChannel是不可以使用Selector的,因为FileChannel是阻塞的
channel.configureBlocking(false);
// 第二个参数指定了我们对 Channel 的什么类型的事件感兴趣
SelectionKey key = channel.register(selector , SelectionKey.OP_READ);
// 也可以使用或运算|来组合多个事件,例如
SelectionKey key = channel.register(selector , SelectionKey.OP_READ | SelectionKey.OP_WRITE);
// 不过值得注意的是,一个 Channel 仅仅可以被注册到一个 Selector 一次, 如果将 Channel 注册到 Selector 多次, 那么其实就是相当于更新 SelectionKey 的 interest set.
一个Channel在Selector注册其代表的是一个SelectionKey事件
SelectionKey的类型包括:
OP_READ:可读事件;值为:1<<0
OP_WRITE:可写事件;值为:1<<2
OP_CONNECT:客户端连接服务端的事件(tcp连接),一般为创建SocketChannel客户端channel;值为:1<<3
OP_ACCEPT:服务端接收客户端连接的事件,一般为创建ServerSocketChannel服务端channel;值为:1<<4
一个Selector内部维护了三组keys:
key set:
当前channel注册在Selector上所有的key;可调用keys()获取
selected-key set:当前channel就绪的事件;可调用selectedKeys()获取
cancelled-key:主动触发SelectionKey#cancel()方法会放在该集合,前提条件是该channel没有被取消注册;不可通过外部方法调用
Selector类中总共包含以下10个方法:
open():创建一个Selector对象
isOpen():是否是open状态,如果调用了close()方法则会返回false
provider():获取当前Selector的Provider
keys():如上文所述,获取当前channel注册在Selector上所有的key
selectedKeys():获取当前channel就绪的事件列表
selectNow():获取当前是否有事件就绪,该方法立即返回结果,不会阻塞;如果返回值>0,则代表存在一个或多个
select(long timeout):selectNow的阻塞超时方法,超时时间内,有事件就绪时才会返回;否则超过时间也会返回
select():selectNow的阻塞方法,直到有事件就绪时才会返回
wakeup():调用该方法会时,阻塞在select()处的线程会立马返回;(ps:下面一句划重点)即使当前不存在线程阻塞在select()处,那么下一个执行select()方法的线程也会立即返回结果,相当于执行了一次selectNow()方法
close(): 用完Selector后调用其close()方法会关闭该Selector,且使注册到该Selector上的所有SelectionKey实例无效。channel本身并不会关闭。
关于SelectionKey
谈到Selector就不得不提SelectionKey,两者是紧密关联,配合使用的;如上文所示,往Channel注册Selector会返回一个SelectionKey对象,
这个对象包含了如下内容:
interest set,当前Channel感兴趣的事件集,即在调用register方法设置的interes set
ready set
channel
selector
attached object,可选的附加对象
interest set
可以通过SelectionKey类中的方法来获取和设置interes set
// 返回当前感兴趣的事件列表
int interestSet = key.interestOps();
// 也可通过interestSet判断其中包含的事件
boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT;
boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;
boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;
boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
// 可以通过interestOps(int ops)方法修改事件列表
key.interestOps(interestSet | SelectionKey.OP_WRITE);
ready set
当前Channel就绪的事件列表
int readySet = key.readyOps();
// 也可通过四个方法来分别判断不同事件是否就绪
key.isReadable(); //读事件是否就绪
key.isWritable(); //写事件是否就绪
key.isConnectable(); //客户端连接事件是否就绪
key.isAcceptable(); //服务端连接事件是否就绪
channel和selector
我们可以通过SelectionKey来获取当前的channel和selector
// 返回当前事件关联的通道,可转换的选项包括:`ServerSocketChannel`和`SocketChannel`
Channel channel = key.channel();
//返回当前事件所关联的Selector对象
Selector selector = key.selector();
attached object
我们可以在selectionKey中附加一个对象:
key.attach(theObject);
Object attachedObj = key.attachment();
或者在注册时直接附加:
SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);