netty学习(一)简述NIO

NIO简介

NIO,New IO(官方),Non-blocking IO(非官方),是 JDK1.4 中引入的一种新的 IO 标准,是一种同步非阻塞 IO。NIO 是以块为单位进行数据处理的,当然块的大小是程序员自己指定的。其相对于 BIO 的以字节/字符为单位所进行的阻塞式处理方式,大大提高了读写效率与并发度。

  • BIO:Blocking IO,同步阻塞 IO
  • NIO:Non-blocking IO,同步非阻塞 IO JDK1.4
  • AIO:异步非阻塞 IO,也称为 NIO2.0 JDK1.7

为什么NIO适合高并发的场景

首先看下面这张BIO的客户端与服务器线程之间的示意图
在这里插入图片描述
有图可知,每一个客户端与服务器建立链接就会产生一个线程,而线程资源是非常宝贵的,而且是有上限的,所以就造成了BIO并发处理的瓶颈。而NIO为了解决这一点使用了下面的模型
在这里插入图片描述

NIO中Channel与线程之间是多对一的关系。而哪一个Channel使用线程是通过selector这一多路复用器来决定的。当一个Channel准备就绪时就通知线程来处理该通道的请求。由于多个Channel对应一个线程,所以NIO是可以处理高并发的。

JAVA NIO中重要的API

java.nio.channels.spi.SelectorProvider

该类主要负责提供选择器以及可以选择的Channel。
该类通过provider()维护了一个全局且唯一的一个SelectorProvider实例,而且该类的所有方法都是线程安全的。

    /**
     * Returns the system-wide default selector provider for this invocation of
     * the Java virtual machine.
     *
     * <p> The first invocation of this method locates the default provider
     * object as follows: </p>
     *
     * <ol>
     *
     *   <li><p> If the system property
     *   <tt>java.nio.channels.spi.SelectorProvider</tt> is defined then it is
     *   taken to be the fully-qualified name of a concrete provider class.
     *   The class is loaded and instantiated; if this process fails then an
     *   unspecified error is thrown.  </p></li>
     *
     *   <li><p> If a provider class has been installed in a jar file that is
     *   visible to the system class loader, and that jar file contains a
     *   provider-configuration file named
     *   <tt>java.nio.channels.spi.SelectorProvider</tt> in the resource
     *   directory <tt>META-INF/services</tt>, then the first class name
     *   specified in that file is taken.  The class is loaded and
     *   instantiated; if this process fails then an unspecified error is
     *   thrown.  </p></li>
     *
     *   <li><p> Finally, if no provider has been specified by any of the above
     *   means then the system-default provider class is instantiated and the
     *   result is returned.  </p></li>
     *
     * </ol>
     *
     * <p> Subsequent invocations of this method return the provider that was
     * returned by the first invocation.  </p>
     *
     * @return  The system-wide default selector 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;
                        }
                    });
        }
    }

后面不管是取得通道还是选择器都是通过这个全局唯一的provider实例来获取的。

    /**
     * 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();
    }
    /**
     * 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();
    }

java.nio.channels.Selector

  • 该类在NIO中充当的是一个多路复用器的角色。
  • 这个类创建有两种方法,一种是Selector的open()方法,一种是SelectorProvider的openSelector()方法,不过第一种方式本质上也是调用的SelectorProvider的openSelector()方法。
  • 选择器被open之后会一直处于open状态,直到调用了Selector的close()方法。
  • 可选择通道是通过SelectionKey的方式向选择器的注册的。一个通道对应一个SelectionKey。
  • Selector一共维护了三个SelectionKey的集合,分别为所有的Key的集合(key set),所有的准备就绪的Key的集合(selected-key set),处理完毕的待从selector中删除的key的集合(cancelled-key set)。在下一次选择时会把这里面的Key从所有Key集合中删除掉。
  • key set元素的添加是在Channel注册到Selector时,删除是在每次选择操作时将包含在cancelled-key set中的元素删除。注意该集合本身是不会直接进行修改的
  • selected-key set中的元素添加是在Selector进行选择操作时添加的,并且永远不会自动移除,需要你手动调用set集合的remove方法或者Set集合Iterator的remove方法进行移除,也就是说在进行业务处理之后你要手动调用remove方法来避免重复的操作。
  • cancelled-key set中的元素在调用SelectionKey的cancle方法时添加,在下一次选择操作时从该集合以及 key set对应的元素进行删除。
  • Selector的选择操作select()采用了poll轮询的方式挨个查询是否有准备就绪的Channel期间执行线程是阻塞状态,当然你可以指定阻塞超时时间,来结束阻塞状态。该方法返回值有可能为0,代表该阶段没有准备就绪的通道。选择操作会对三个元素集合产生影响。会删除cancelled-key set中的元素以及key set中对应的元素,会将准备就绪的通道放入selected-key set中。

java.nio.channels.SelectionKey

  • 每一个Channel注册至Selector中都会产生一个SelectionKey。
  • 一个SelectionKey的取消有三种方式,SelectionKey的取消操作,通道的关闭,以及Selector关闭。
  • 一个SelectionKey包含一个或者多个感兴趣的操作,即Selector轮询时监听的事件。包括OP_READ(有消息到达可以被读取)。OP_WRITE(可以被写入)。OP_CONNECT(套接字通道已经准备好完成它的连接)。OP_ACCEPT(有通道进行接入)

NIO服务端处理消息的流程

  1. 打开一个服务端的通道,并选择监听的端口。
  2. 打开一个Selecor
  3. 将通道注册到Selector中,并选择需要监听的事件
  4. Selector以轮询的方式查找是否有准备就绪的通道,并将该通道的SelectionKey放入selected-key set中。
  5. 将处理过的通道的key从selected-key set中移除
  6. 继续第4步直到Channel或者Selector关闭
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加班狂魔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值