Java小强个人技术博客站点    手机版
当前位置: 首页 >> 理论 >> 并发编程之Java中Selector

并发编程之Java中Selector

3590 理论 | 2022-9-28

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

C/C++许多年前就已经有 select()和 poll()这两个POSIX(可移植性操作系统接口)系统调用可供使用。许多os也提供相似的功能,但对Java 程序员来说,就绪选择功能直到 JDK 1.4 才成为可行方案。

NIO.jpg


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);


END

推荐您阅读更多有关于“ 网络编程 nio 并发编程 Selector Channel ”的文章

上一篇:Future机制 下一篇:并发编程之AtomicInteger,AtomicLong,LongAdder

猜你喜欢

发表评论: