jdk源码解析八之NIO(selector)
文章目录
- 示例代码
- register
- open
- select
示例代码
ServerSocketChannel serverChannel = ServerSocketChannel.open();
ServerSocket serverSocket = serverChannel.socket();
Selector selector = Selector.open();
serverSocket.bind (new InetSocketAddress (port));
serverChannel.configureBlocking (false);
serverChannel.register (selector, SelectionKey.OP_ACCEPT);
while (true) {
int n = selector.select();
if (n == 0) {
continue;
}
Iterator it = selector.selectedKeys().iterator();
while (it.hasNext()) {
SelectionKey key = (SelectionKey) it.next();
if (key.isAcceptable()) {
ServerSocketChannel server =
(ServerSocketChannel) key.channel();
SocketChannel channel = server.accept();
}
if (key.isReadable()) {
//...
}
it.remove();
}
}
重点只需要关注通道的register和选择器的select方法
register
public final SelectionKey register(Selector sel, int ops,
Object att)
throws ClosedChannelException
{
synchronized (regLock) {
if (!isOpen())
throw new ClosedChannelException();
//是否支持特定操作
if ((ops & ~validOps()) != 0)
throw new IllegalArgumentException();
//必须支持非阻塞
if (blocking)
throw new IllegalBlockingModeException();
//从选择器keys中查找与通道匹配key
SelectionKey k = findKey(sel);
//查找到了
if (k != null) {
//修改特定操作
k.interestOps(ops);
//重新设置附件对象
k.attach(att);
}
if (k == null) {
// New registration
synchronized (keyLock) {
if (!isOpen())
throw new ClosedChannelException();
//注册
k = ((AbstractSelector)sel).register(this, ops, att);
//添加到缓存
addKey(k);
}
}
return k;
}
}
private SelectionKey findKey(Selector sel) {
synchronized (keyLock) {
if (keys == null)
return null;
//从缓存的keys中获取匹配
for (int i = 0; i < keys.length; i++)
if ((keys[i] != null) && (keys[i].selector() == sel))
return keys[i];
return null;
}
}
private void addKey(SelectionKey k) {
assert Thread.holdsLock(keyLock);
int i = 0;
if ((keys != null) && (keyCount < keys.length)) {
// Find empty element of key array
for (i = 0; i < keys.length; i++)
if (keys[i] == null)
break;
} else if (keys == null) {
//第一次初始化容量3
keys = new SelectionKey[3];
} else {
// Grow key array
//空间不足,扩容一倍
int n = keys.length * 2;
SelectionKey[] ks = new SelectionKey[n];
for (i = 0; i < keys.length; i++)
ks[i] = keys[i];
keys = ks;
i = keyCount;
}
keys[i] = k;
keyCount++;
}
protected final SelectionKey register(AbstractSelectableChannel ch,
int ops,
Object attachment)
{
if (!(ch instanceof SelChImpl))
throw new IllegalSelectorException();
//新建key,记录选择器和通道
SelectionKeyImpl k = new SelectionKeyImpl((SelChImpl)ch, this);
k.attach(attachment);
synchronized (publicKeys) {
//注册,与平台相关
implRegister(k);
}
//设置感兴趣的特定操作
k.interestOps(ops);
return k;
}
open
public static Selector open() throws IOException {
//与平台相关
return SelectorProvider.provider().openSelector();
}
public static SelectorProvider provider() {
synchronized (lock) {
if (provider != null)
return provider;
return AccessController.doPrivileged(
new PrivilegedAction<SelectorProvider>() {
public SelectorProvider run() {
//通过配置的java.nio.channels.spi.SelectorProvider值注入自定义的SelectorProvider
if (loadProviderFromProperty())
return provider;
//通过ServiceLoad注入,然后获取配置的第一个服务
if (loadProviderAsService())
return provider;
//创建的Provider与平台相关
provider = sun.nio.ch.DefaultSelectorProvider.create();
return provider;
}
});
}
}
select
public int select(long timeout)
throws IOException
{
if (timeout < 0)
throw new IllegalArgumentException("Negative timeout");
return lockAndDoSelect((timeout == 0) ? -1 : timeout);
}
//返回从上一个select( )调用之后进入就绪状态的通道的数量
public int select() throws IOException {
return select(0);
}
/*
1.已取消的键的集合将会被检查。如果它是非空的,每个已取消的键的集合中的键将从另外两个集合中移除,
并且相关的通道将被注销。这个步骤结束后,已取消的键的集合将是空的。
2.已注册的键的集合中的键的interest集合将被检查。在这个步骤中的检查执行过后,
对interest集合的改动不会影响剩余的检查过程。
一旦一个键处于已选择的键的集合中,这个键的ready集合将只会被设置,而不会被清理。
当通道上的至少一个感兴趣的操作就绪时,键的ready集合就会被清空,并且当前已经就绪的操作将会被添加到ready集合中。
该键之后将被添加到已选择的键的集合中。
*/
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);
}
}
}
}
还没有评论,来说两句吧...