I/O(input/output)内存模型(2)

我们了解了I/O的基本概念,之后需要讨论的就是比如Java支持三大I/O模型,BIO,NIO和AIO

1.BIO

        BIO就是Blocking I/O, 同步阻塞型IO,数据的读取写入必须阻塞在一个线程内等待其完成。我们很熟悉的java输入和输出流都是基于BIO去实现的,如InputStream,OutputStream等。

        (这种Block是不会影响同时运行的其他程序(进程)的,因为现代操作系统都是多任务的,任务之间的切换是抢占式的。这里Block只是指Block当前的进程。)

        BIO顾名思义,可以理解成单线程操作。当客户端发起一个请求的时候,我们的服务器创建一个线程来处理这个请求,因为服务器无法预知客户端是否发送了请求,所以在收到数据之前,服务器线程只能被挂起,无法执行操作,也无法执行别的操作,只能等待客户端数据。如果这个线程没有接受到返回的数据,就会一直阻塞等待数据返回。

        当处于高并发状态的时候,网络服务为了同时响应多个并发的网络请求,必须实现为多线程的。每个线程处理一个网络请求。线程数也会随着并发连接数线性增长。这样,BIO模型会创建大量的线程,这样就会出现两个大问题:

        1.线程多,每个线程就会占用内存作为线程栈,大量线程积压等待会导致内存占用疯狂增加。

        2.线程越多,Context Switch就越多,而Context Switch是一个比较重的操作,会无谓浪费大量的CPU。

优缺点:

        优点明显,就是一一对应,程序简单好理解,在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O。

        缺点也很明显,占用资源,性能不能达到最优。

 优化:

        BIO我们一般会使用线程池的方式进行优化。线程池既能并发的处理请求,又不会产生大量线程,乍一看是一个很好的方法,但是线程池会限制最大并发的连接数如果我的最大线程都占用了,也没办法响应更多的请求了。

        所以造成BIO无法最佳性能的原因在哪里呢?在于服务器无法预知客户端是否发送了请求。那么要是操作IO接口时,操作系统能够总是直接告诉有没有数据,那就不需要等待数据,解放了线程,这就是NIO的实现基础。

2.NIO

        NIO是non-blocking I/O 或者 New IO ,是同步非阻塞的IO模型,实现模式是一个线程处理多个请求,采用轮询机制,不断的尝试有没有数据到达。如果连接有IO请求,就创建线程进行处理。如果没有就等一小会再试。这样就实现了NIO的非阻塞机制,当线程读取不到客户端发送的数据也不会阻塞,这样该线程就可以做其他的事情不会被卡死。

        这样会带来两个新问题:

        1.如果有大量文件描述符都要等,那么就得一个一个的read。这会带来大量的Context Switch(read是系统调用,每调用一次就得在用户态和核心态切换一次)

        2.轮询休息的时间不好把握。这里是要猜多久之后数据才能到。等待时间设的太长,程序响应延迟就过大;设的太短,就会造成过于频繁的重试,干耗CPU。

        所以在NIO模型中,IO多路复用器(IO Multiplexing) 用于解决这个问题。

关注 IO多路复用器之前,就需要关注三个新概念:

1.Buffer(缓冲区)

        NIO以块(buffer)的方式处理数据,Buffer是一个对象,它包含一些要写入或者要读出的数据。NIO在读取数据时,它是直接读到缓冲区中的; 在写入数据时,写入到缓冲区中。任何时候访问NIO中的数据,都是通过缓冲区进行操作。相比较于BIO的 Stream I/O效率更高。

2.Channel (通道)

        NIO 通过Channel(通道)进行读写。通道与Buffer进行交互,来实现双向读写(异步读写的实现),而BIO以来的Stream(流),只能是单向的读/写。

    NIO的读写都是从Channel开始的:

  • 从通道进行数据读取 :创建一个缓冲区,然后请求通道读取数据。

  • 从通道进行数据写入 :创建一个缓冲区,填充数据,并要求通道写入数据。

3.Selectors(选择器/多路复用器)

        选择器,也叫多路复用器,一般使用单个线程处理多个通道,也就是多个通道可以注册到同一个Selector。

        Selector能够监测到channel上是否有读/写事件发生,从而获取事件和对事件进行处理,所以Selector切到哪个channel是由事件决定的。

        因此,它需要较少的线程来处理这些通道。为了提高系统效率,线程之间的切换使用选择器来实现。也就是告诉你有哪些流有事件了,其他没有事件的流呢,就可以空闲出来处理别的问题。

IO多路复用就是以上概念的集合实现,使用多路复用的NIO模型,如下图:

ref:一.Netty入门到超神系列-BIO、NIO、AIO的认识_墨家@俏如来-CSDN博客

        相比BIO,NIO多了一个选择,分发的过程,释放了阻塞的线程。现在用的很多的Netty也是基于NIO的。

现在也很明显了,BIO 和 NIO 的区别在于:

    1.BIO以流的方式处理数据,NIO以块(buffer)的方式处理数据,块的IO效率高于流的IO效率
    2.BIO是阻塞IO,NIO是非阻塞IO
    3.BIO使用字符流,或者字节流进行操作,NIO基于channel通道和buffer缓冲区进行操作
    4.BIO适用于并发低的业务场景,NIO适用于并发高的业务场景

        前面我们说到 Channel , channel运用了Buffer,可以实现异步读写,前面的NIO和BIO,都是同步I/O,那么我们有没有可能根据Channel的特性实现异步读写呢?AIO就出现了。

3.AIO

        目前 AIO 还没有广泛应用。

        AIO就是Asynchronous I/O,是异步非阻塞IO。在进行 I/O 编程中,常用到两种模式:Reactor和 Proactor,Java 的 NIO 就是 Reactor ,AIO采用 Proactor 模式。AIO是对于NIO的增强(NIO2.0),AIO 引入异步通道的概念,简化了程序编写,有效的请求才启动线程,它的特点是先由操作系统完成后才通知服务端程序启动线程去处理,一般适用于连接数较多且连接时间较长的应用。(之后再深入了解一下)

以上就是BIO,NIO和AIO的概念总结了,其他的I/O模型有机会再继续深入了解吧

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

PigeonEssence

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

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

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

打赏作者

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

抵扣说明:

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

余额充值