NIO 之 ByteBuffer实现原理
NIO 之 Channel实现原理
NIO 之 Selector实现原理
Selector允许单线程处理多个 Channel。如果你的应用打开了多个连接(通道),但每个连接的流量都很低,使用Selector就会很方便。例如,在一个聊天服务器中。
这是在一个单线程中使用一个Selector处理3个Channel的图示:
要使用Selector,得向Selector注册Channel,然后调用它的select()方法。这个方法会一直阻塞到某个注册的通道有事件就绪。一旦这个方法返回,线程就可以处理这些事件,事件的例子有如新连接进来,数据接收等。
仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通道。事实上,可以只用一个线程处理所有的通道。对于操作系统来说,线程之间上下文切换的开销很大,而且每个线程都要占用系统的一些资源(如内存)。因此,使用的线程越少越好。
Selector能够在单个线程中处理多个通道,这样可以减少多个线程造成上下文切换问题。
public abstract class Selector implements Closeable {
protected Selector() { }
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
public abstract boolean isOpen();
public abstract SelectorProvider provider();
public abstract Set keys();
public abstract Set selectedKeys();
public abstract int selectNow() throws IOException;
public abstract int select(long timeout) throws IOException;
public abstract int select() throws IOException;
public abstract Selector wakeup();
public abstract void close() throws IOException;
Selector 是个抽象类,提供一个静态的方法获取Selector子类SelectorImpl的实例。
下面分析Selector的几个方法
该方法是在 Channel的register方法中调用的。具体详见NIO 之 Channel实现原理
1. 通过channel和selector构造一个SelectionKey的实例。
2. SelectionKey 注册感兴趣的事件
这四种事件用SelectionKey的四个常量来表示:
SelectionKey.OP_CONNECT
SelectionKey.OP_ACCEPT
SelectionKey.OP_READ
SelectionKey.OP_WRITE
不同的 Channel 注册到 Selector 后,就可以随时查询 Selector ,找出哪些 Channel 已经准备好可以进行处理。Channel 可能准备好上面注册到 Selector 感兴趣事件中的一个或多个。
public int select() throws IOException {
return select(0);
}
public int select(long timeout)
throws IOException
{
if (timeout < 0)
throw new IllegalArgumentException("Negative timeout");
return lockAndDoSelect((timeout == 0) ? -1 : timeout);
}
public int selectNow() throws IOException {
return lockAndDoSelect(0);
}
上面三个 select方法底层都是调用 lockAndDoSelect 方法。
lockAndDoSelect方法的参数值 说明:
-1 : 一直阻塞,直到有就绪的 Channel 可处理
0 : 不阻塞
0: 表示阻塞多长时间
获取所有注册到 Selector 上的 SelectionKey
public Set keys() {
if (!isOpen() && !Util.atBugLevel("1.4")) throw new ClosedSelectorException();
return publicKeys;
}
获取所有注册到 Selector 上就绪 Channel 的 SelectionKey 信息。
public Set selectedKeys() {
if (!isOpen() && !Util.atBugLevel("1.4")) throw new ClosedSelectorException();
return publicSelectedKeys;
}
SelectionKey 类结构如下:
public abstract class SelectionKey {
protected SelectionKey() { }
public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
public static final int OP_ACCEPT = 1 << 4;
//附件信息
private volatile Object attachment = null;
....
}
public abstract Selector selector()
获取seletor对象
public abstract void cancel()
从 Selector 中取消注册该Channel
public abstract int interestOps()
获取该chennel 注册到 selector 上的事件
public abstract SelectionKey interestOps(int ops)
修改注册到 selector 上的事件
public abstract int readyOps()
是否读就绪
读就绪不等于可读,如果没有注册读事件是不能读的。
public final boolean isReadable()
判断是否可读
public final boolean isWritable()
是否可写
public final boolean isConnectable()
是否已经连接
public final Object attach(Object ob)
添加附件信息
public final Object attachment()
获取附件信息
本人简书blog地址:http://www.jianshu.com/u/1f0067e24ff8
点击这里快速进入简书
GIT地址:http://git.oschina.net/brucekankan/
点击这里快速进入GIT