• Java I/O模型


    本文转发自技术世界原文链接 http://www.jasongj.com/java/nio_reactor/

    同步 vs. 异步

    同步I/O 

    每个请求必须逐个地被处理,一个请求的处理会导致整个流程的暂时等待,

    这些事件无法并发地执行。用户线程发起I/O请求后需要等待或者轮询内核I/O操作完成后才能继续执行。

    同步意味着一个任务的某个处理过程会对多个线程在用串行化处理

    异步I/O 

    多个请求可以并发地执行,一个请求或者任务的执行不会导致整个流程的暂时等待。

    用户线程发起I/O请求后仍然继续执行,当内核I/O操作完成后会通知用户线程,或者调用用户线程注册的回调函数。

    异步则意味着某个处理过程可以允许多个线程同时处理。


    阻塞 vs. 非阻塞

    阻塞 

    某个请求发出后,由于该请求操作需要的条件不满足,请求操作一直阻塞,不会返回,直到条件满足。

    非阻塞 

    请求发出后,若该请求需要的条件不满足,则立即返回一个标志信息告知条件不满足,而不会一直等待。

    一般需要通过循环判断请求条件是否满足来获取请求结果。

    需要注意的是,阻塞并不等价于同步,而非阻塞并非等价于异步。

    事实上这两组概念描述的是I/O模型中的两个不同维度。

    同步和异步着重点在于多个任务执行过程中,后发起的任务是否必须等先发起的任务完成之后再进行。

    而不管先发起的任务请求是阻塞等待完成,还是立即返回通过循环等待请求成功。

    阻塞和非阻塞重点在于请求的方法是否立即返回(或者说是否在条件不满足时被阻塞)


    Java中四种I/O模型

    Java最早提供的blocking I/O即是阻塞I/O,

    
    如上文所述,阻塞I/O下请求无法立即完成,则保持阻塞。


    阻塞I/O分为如下两个阶段:
    阶段1:等待数据就绪。网络 I
    /O 的情况就是等待远端数据陆续抵达;磁盘I/O的情况就是等待磁盘数据从磁盘上读取到内核态内存中。
    阶段2:数据拷贝。出于系统安全,用户态的程序没有权限直接读取内核态内存,因此内核负责把内核态内存中的数据拷贝一份到用户态内存中。

    而NIO即是非阻塞I/O

    非阻塞I/O请求包含如下三个阶段
    
    1)socket设置为 NONBLOCK(非阻塞)就是告诉内核,

      当所请求的I
    /O操作无法完成时,不要将线程睡眠,而是返回一个错误码(EWOULDBLOCK) ,这样请求就不会阻塞。
    2)I
    /O操作函数将不断的测试数据是否已经准备好,如果没有准备好,继续测试,直到数据准备好为止。

      整个I/O 请求的过程中,虽然用户线程每次发起I/O请求后可以立即返回,但是为了等到数据,仍需要不断地轮询、重复请求,消耗了大量的 CPU 的资源。
    3)数据准备好了,从内核拷贝到用户空间。

    一般很少直接使用这种模型,而是在其他I
    /O模型中使用非阻塞I/O 这一特性。这种方式对单个I/O 请求意义不大,但给I/O多路复用提供了条件。

    同时通过NIO实现的Reactor模式即是 I/O复用模型的实现,

    I/O多路复用(异步阻塞 I/O)
    I
    /O多路复用会用到select或者poll函数,这两个函数也会使线程阻塞,


    但是和阻塞I/O所不同的是,这两个函数可以同时阻塞多个I/O操作。

    而且可以同时对多个读操作,多个写操作的I/O函数进行检测,直到有数据可读或可写时,才真正调用I/O操作函数。 从流程上来看,使用select函数进行I/O请求和同步阻塞模型没有太大的区别,甚至还多了添加监视Channel,以及调用select函数的额外操作,增加了额外工作。

    但是,使用 select以后最大的优势用户可以在一个线程内同时处理多个Channel的I/O请求。

    用户可以注册多个Channel,然后不断地调用select读取被激活的Channel,即可达到在同一个线程内同时处理多个I/O请求的目的。

    而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。 调用select
    /poll该方法由一个用户态线程负责轮询多个Channel,直到某个阶段1的数据就绪,再通知实际的用户线程执行阶段2的拷贝。


    通过一个专职的用户态线程执行非阻塞I/O轮询,模拟实现了阶段一的异步化。

    通过AIO实现的Proactor模式即是异步I/O模型的实现

    调用AIO_read 函数,告诉内核描述字,缓冲区指针,缓冲区大小,文件偏移以及通知的方式,然后立即返回。

    当内核将数据拷贝到缓冲区后,再通知应用程序。所以异步I/O模式下,阶段1和阶段2全部由内核完成,完成不需要用户线程的参与。
  • 相关阅读:
    【经典】5种IO模型 | IO多路复用
    Python3 与 C# 网络编程之~ 网络基础篇
    说说GIL
    工作环境换成Ubuntu18.04小记
    下一代微服务 ~ Service Mesh
    Python3 与 C# 并发编程之~ 线程篇
    Python3 与 C# 并发编程之~ 进程篇
    Python3 与 C# 并发编程之~ Net篇
    Python3 与 C# 扩展之~基础拓展
    Pycharm For Linux
  • 原文地址:https://www.cnblogs.com/ccEmma/p/8624511.html
Copyright © 2020-2023  润新知