概述
java NIO属于epoll水平触发
NIO调用 | linux epoll实现 |
Selector.open() | epoll_create |
ServerSocketChannel#register() | 将事件存储EpollArrayWrapper的成员变量eventsLow和eventsHigh中 |
Selector#select() | 从eventsLow和eventsHigh中取出事件,调用epoll_ctl ,设置为水平触发, 并epoll_wait |
ServerSocketChannel的使用示例如下:
//1. 获取服务端通道
ServerSocketChannel ssChannel = ServerSocketChannel.open();
ssChannel.bind(new InetSocketAddress(9898));
//2. 设置为非阻塞模式
ssChannel.configureBlocking(false);
//3. 打开一个监听器
Selector selector = Selector.open();
//4. 向监听器注册接收事件
ssChannel.register(selector, SelectionKey.OP_ACCEPT);
while (selector.select() > 0) {
//5. 获取监听器上所有的监听事件值
Iterator<SelectionKey> it = selector.selectedKeys().iterator();
//6. 如果有值
while (it.hasNext()) {
//7. 取到SelectionKey
SelectionKey key = it.next();
//8. 根据key值判断对应的事件
if (key.isAcceptable()) {
//9. 接入处理
SocketChannel socketChannel = ssChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
//10. 可读事件处理
SocketChannel channel = (SocketChannel) key.channel();
readMsg(channel);
}
//11. 移除当前key
it.remove();
}
}
ServerSocketChannel.open()方法源码分析
底层是调用SelectorProvider#openServerSocketChannel()方法
/**
* Opens a server-socket channel.
*
* <p> The new channel is created by invoking the {@link
* java.nio.channels.spi.SelectorProvider#openServerSocketChannel
* openServerSocketChannel} method of the system-wide default {@link
* java.nio.channels.spi.SelectorProvider} object.
*
* <p> The new channel's socket is initially unbound; it must be bound to a
* specific address via one of its socket's {@link
* java.net.ServerSocket#bind(SocketAddress) bind} methods before
* connections can be accepted. </p>
*
* @return A new socket channel
*
* @throws IOException
* If an I/O error occurs
*/
public static ServerSocketChannel open() throws IOException {
return SelectorProvider.provider().openServerSocketChannel();
}
SelectorProvider#openServerSocketChannel()方法
底层是创建一个ServerSocketChannelImpl的实例。
public abstract class SelectorProviderImpl extends SelectorProvider {
public SelectorProviderImpl() {
}
.........
public abstract AbstractSelector openSelector() throws IOException;
public ServerSocketChannel openServerSocketChannel() throws IOException {
return new ServerSocketChannelImpl(this);
}
public SocketChannel openSocketChannel() throws IOException {
return new SocketChannelImpl(this);
}
}
Selector.open()方法源码分析
底层是调用SelectorProvider#openSelector()方法
/**
* Opens a selector.
*
* <p> The new selector is created by invoking the {@link
* java.nio.channels.spi.SelectorProvider#openSelector openSelector} method
* of the system-wide default {@link
* java.nio.channels.spi.SelectorProvider} object. </p>
*
* @return A new selector
*
* @throws IOException
* If an I/O error occurs
*/
public static Selector open() throws IOException {
return SelectorProvider.provider().openSelector();
}
SelectorProvider#openSelector()方法
底层是创建一个EpollSelectorImpl的实例。
public class EPollSelectorProvider extends SelectorProviderImpl {
public EPollSelectorProvider() {
}
public AbstractSelector openSelector() throws IOException {
return new EPollSelectorImpl(this);
}
public Channel inheritedChannel() throws IOException {
return InheritedChannel.getChannel();
}
}
ServerSocketChannel#register()方法源码分析
register方法是其父类AbstractSelectableChannel的方法。
底层其实是调用了Selector的register方法。
/**
* Registers this channel with the given selector, returning a selection key.
*
* <p> This method first verifies that this channel is open and that the
* given initial interest set is valid.
*
* <p> If this channel is already registered with the given selector then
* the selection key representing that registration is returned after
* setting its interest set to the given value.
*
* <p> Otherwise this channel has not yet been registered with the given
* selector, so the {@link AbstractSelector#register register} method of
* the selector is invoked while holding the appropriate locks. The
* resulting key is added to this channel's key set before being returned.
* </p>
*
* @throws ClosedSelectorException {@inheritDoc}
*
* @throws IllegalBlockingModeException {@inheritDoc}
*
* @throws IllegalSelectorException {@inheritDoc}
*
* @throws CancelledKeyException {@inheritDoc}
*
* @throws IllegalArgumentException {@inheritDoc}
*/
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();
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();
//调用Selector#register()方法
k = ((AbstractSelector)sel).register(this, ops, att);
//返回的SelectionKey添加到其成员变量SelectionKey[] keys中
addKey(k);
}
}
return k;
}
}
Selector#register()方法
前面说过,Selector.open()方法会创建一个SelectorImpl实例。所以,在这里是调用SelectorImpl的register方法。
该方法其实是创建了一个SelectionKeyImpl实例,并作为register方法的返回值。
protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {
if (!(var1 instanceof SelChImpl)) {
throw new IllegalSelectorException();
} else {
SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);
var4.attach(var3);
synchronized(this.publicKeys) {
this.implRegister(var4);
}
var4.interestOps(var2);
return var4;
}
}
Selector#select()方法源码分析
底层是调用了SelectorImpl#select()方法。
public int select(long var1) throws IOException {
if (var1 < 0L) {
throw new IllegalArgumentException("Negative timeout");
} else {
return this.lockAndDoSelect(var1 == 0L ? -1L : var1);
}
}
private int lockAndDoSelect(long var1) throws IOException {
synchronized(this) {
if (!this.isOpen()) {
throw new ClosedSelectorException();
} else {
int var10000;
synchronized(this.publicKeys) {
synchronized(this.publicSelectedKeys) {
//调用了EpollSelectorImpl的doSelect()方法
var10000 = this.doSelect(var1);
}
}
return var10000;
}
}
}
EpollSelectorImpl#doSelect()方法
底层是调用了EpollArrayWrapper#poll()方法进行轮询。
protected int doSelect(long var1) throws IOException {
if (this.closed) {
throw new ClosedSelectorException();
} else {
this.processDeregisterQueue();
try {
this.begin();
//调用了EpollArrayWrapper的poll方法
this.pollWrapper.poll(var1);
} finally {
this.end();
}
this.processDeregisterQueue();
int var3 = this.updateSelectedKeys();
if (this.pollWrapper.interrupted()) {
this.pollWrapper.putEventOps(this.pollWrapper.interruptedIndex(), 0);
synchronized(this.interruptLock) {
this.pollWrapper.clearInterrupted();
IOUtil.drain(this.fd0);
this.interruptTriggered = false;
}
}
return var3;
}
}
EpollArrayWrapper#poll()方法最终是调用linux的epoll()函数,可参阅java NIO之SelectorProvider类源码分析
待续:
AbtractSelectableChannel#configureBlocking()的jvm源码分析
/**
* Adjusts this channel's blocking mode.
*
* <p> If the given blocking mode is different from the current blocking
* mode then this method invokes the {@link #implConfigureBlocking
* implConfigureBlocking} method, while holding the appropriate locks, in
* order to change the mode. </p>
*/
public final SelectableChannel configureBlocking(boolean block)
throws IOException
{
synchronized (regLock) {
if (!isOpen())
throw new ClosedChannelException();
if (blocking == block)
return this;
if (block && haveValidKeys())
throw new IllegalBlockingModeException();
implConfigureBlocking(block);
blocking = block;
}
return this;
}
SocketChannelImpl#configureBlocking()方法
tip: ServerSocketChannelImpl#configureBlocking()方法 也是相同代码
protected void implConfigureBlocking(boolean var1) throws IOException {
IOUtil.configureBlocking(this.fd, var1);
}
IOUtil.configureBlocking()方法
public static native void configureBlocking(FileDescriptor var0, boolean var1) throws IOException;