NIO原理解析

NIO核心图如下:

NIO原理解析_第1张图片
将所有通道注册到Selector上,通过Buffer来执行对数据的操作

1.缓冲区Buffer

1.1 设计原理

类图:
NIO原理解析_第2张图片
可以看到,除了boolean,其他七个基本类型都有
MappedByteBuffer:能够实现内存与磁盘的同步更新

本质上是一个数组,不过做了一些封装;任何时候访问NIO中的数据,都是将它放到缓冲区中,而BIO中则是直接读取到Stream中

相关属性、方法:

capacity:容量
limit:可操作范围
mark:https://www.cnblogs.com/qiumingcheng/p/9486925.html
position:get/put的当前位置/索引

clear():清空
flip():固定当前缓冲区

下面以几个图来演示一下状态变化:
1.初始,allocate(10):
NIO原理解析_第3张图片
2.put()四次数据:
NIO原理解析_第4张图片
3.需要将缓冲区的数据取出来解析了,则需要调用flip(),使我们只能操作从0到4的数据,如果超出这个范围的get,报错为BufferUnderflowException,put报错为BufferOverflowException,这是一个很牛逼的设计理念:
NIO原理解析_第5张图片
4.get()开始取数据,get()四次:
NIO原理解析_第6张图片
5.调用clear(),回到初始状态:
NIO原理解析_第7张图片

2.通道Channel

在NIO中,所有的IO操作都从一个Channel开始。Channel中的数据要么写入Buffer,要么从Buffer读取
类图:
NIO原理解析_第8张图片

3.反应堆

工作原理图:
NIO原理解析_第9张图片
反应堆是一个概念,下面看看其落地实现:

3.1 Selector

架构本质就是一个线程负责轮询,分发:
NIO原理解析_第10张图片

源码分析:

涉及的相关类以及方法:
FileChannel ServerSocketChannel SocketChannel ByteBuffer
ByteBuffer方法:warp#slice#allocate#allocateDirect#asReadOnlyBuffer#MapperByteBuffer#put#get#flip#clear

1.1 selector = Selector.open();:

//java.nio.channels.Selector#open
public static Selector open() throws IOException {
    return SelectorProvider.provider().openSelector();
}

1.1.1 SelectorProvider.provider():

//java.nio.channels.spi.SelectorProvider#provider
public static SelectorProvider provider() {
    synchronized (lock) {
        if (provider != null)
            return provider;
        return AccessController.doPrivileged(
            new PrivilegedAction<SelectorProvider>() {
                public SelectorProvider run() {
                    if (loadProviderFromProperty())
                        return provider;
                    if (loadProviderAsService())
                        return provider;
                    provider = sun.nio.ch.DefaultSelectorProvider.create();
                    return provider;
                }
            });
    }
}
//sun.nio.ch.DefaultSelectorProvider#create
public static SelectorProvider create() {
    return new WindowsSelectorProvider();
}

provider:NIO使用了epoll模型,体现在Selector,而provider就是将Selector跟操作系统的epoll联系起来的关键点。

1.1.2 SelectorProvider.provider().openSelector();:

//sun.nio.ch.WindowsSelectorProvider#openSelector
public AbstractSelector openSelector() throws IOException {
    return new WindowsSelectorImpl(this);//调用操作系统底层的API
}
//sun.nio.ch.WindowsSelectorImpl#WindowsSelectorImpl
//epoll模型,此处也开启了pipe
WindowsSelectorImpl(SelectorProvider var1) throws IOException {
    super(var1);
    this.wakeupSourceFd = ((SelChImpl)this.wakeupPipe.source()).getFDVal();
    SinkChannelImpl var2 = (SinkChannelImpl)this.wakeupPipe.sink();
    var2.sc.socket().setTcpNoDelay(true);
    this.wakeupSinkFd = var2.getFDVal();
    this.pollWrapper.addWakeupSocket(this.wakeupSourceFd, 0);
}
//java.nio.channels.spi.AbstractSelector#AbstractSelector
//为WindowsSelectorImpl的父类SelectorImpl的父类
protected AbstractSelector(SelectorProvider provider) {
    this.provider = provider;
}

1.2 ServerSocketChannel.open();:

//java.nio.channels.ServerSocketChannel#open
public static ServerSocketChannel open() throws IOException {
    return SelectorProvider.provider().openServerSocketChannel();
}
//provider()方法同上
//sun.nio.ch.SelectorProviderImpl#openServerSocketChannel
public ServerSocketChannel openServerSocketChannel() throws IOException {
    return new ServerSocketChannelImpl(this);
}
//sun.nio.ch.ServerSocketChannelImpl#ServerSocketChannelImpl
ServerSocketChannelImpl(SelectorProvider var1) throws IOException {
    super(var1);
    this.fd = Net.serverSocket(true);
    this.fdVal = IOUtil.fdVal(this.fd);
    this.state = 0;
}

1.3 server.register(selector, SelectionKey.OP_ACCEPT);:

把Selector和Channel绑定在一起了,也就是把new ServerSocketChannel时创建的FD与Selector绑定在了一起

到这里,Server端就启动完成了,主要创建的对象为:
NIO原理解析_第11张图片

1.4 selector.select();:

一直深入,最终可以看到:

protected int doSelect(long var1) throws IOException {
	this.subSelector.poll();//轮询pollWrapper中保存的FD,调用native方法poll0
	return this.updateSelectedKeys();
}

解释:
NIO原理解析_第12张图片

你可能感兴趣的:(Java基础)