首页 > 编程语言 >Java 面试题 02 - IO

Java 面试题 02 - IO

时间:2022-10-06 17:23:32浏览次数:48  
标签:02 面试题 Java Reactor 线程 IO 连接 select socket

select、poll、epoll

缓存 IO

数据传输过程中,会先被拷贝到内核的缓冲区中,然后再从缓冲区拷贝到应用程序的地址空间。这些拷贝操作的开销是很大的。

阻塞 / 非阻塞 vs 同步 / 异步

  • 阻塞 / 非阻塞指的是 程序 请求 IO 操作后,如果资源没有准备好,程序该如何处理:阻塞是指程序等待;非阻塞是指程序继续执行(同时不断轮询 IO 资源是否准备好)。
  • 同步 / 异步指的是 操作系统 在收到程序的 IO 请求后,如果资源没有准备好,操作系统该如何响应:同步会到资源准备好时才进行响应;异步会立即响应,当资源准备好时会用事件机制通知程序。

5 种 IO 模型

  • 同步阻塞:应用进程被阻塞,直到数据复制到进程缓冲区时才返回。
  • 同步非阻塞:应用进程执行系统调用之后,内核返回一个错误码,表示 IO 资源尚未准备好;然后应用程序继续执行其他代码,同时不断 轮询,查看 IO 是否就绪。
  • 多路复用 IO:将系统调用拆分为多个组件,让单个进程能够处理多个 IO 事件。select、poll、epoll 都是多路复用 IO 的实现。多路复用的优势是能处理更多连接,而对单个连接的处理速度并没有加快。
  • 信号驱动 IO:应用进程执行 sigaction 系统调用,内核立即返回,应用进程可以继续执行;内核在数据到达时向应用进程发送 SIGIO 信号,应用进程收到后在 信号处理程序 中调用 recvfrom 将数据从内核复制到应用进程中。
  • 异步 IO:用户进程执行 aio_read 系统调用,内核直接返回,然后用户进程继续执行;等到数据就绪时,内核直接复制数据到进程,然后向进程发送通知。(异步 IO 中由 内核负责将数据拷贝到用户进程,同步 IO 是由进程自己来拷贝数据。)

select、poll、epoll 的区别

文件描述符(file descriptor):用于表述指向文件的引用,形式上是一个非负整数,实际上它是一个索引值,指向内核为每个进程所维护的打开文件的记录表,当程序打开一个文件或者创建一个文件时,内核向进程返回一个文件描述符。

select 函数监视的文件描述符有三类:writefds、readfds、exceptfds,调用 select 后会阻塞,直到有描述符就绪(socket 可读、可写或异常发生时)或者超时,函数才返回。阻塞期间,如果某个客户端发送了数据,socket 收到数据后需要唤醒 select,但 select 只知道发生了事件,不知道具体是哪个 socket 发生了事件,所以 需要遍历所有文件描述符(socket)。select 在几乎所有平台上都能用,但是 单个进程能监视的文件描述符的数量有限,在 Linux 上一般为 1024.

poll 用一个 pollfd 类型的结构来监视文件描述符和事件,其中包含文件描述符、要监视的事件、发生的事件三个字段。pollfd 监视的文件描述符没有上限。和 select 一样,有事件发生时,也需要遍历所有 socket 才能找到目标事件。

epoll 解决了 select/poll 的两个问题:

  • 每次调用 select 都会把监控的 fds 复制到内核里。epoll 提供了 epoll_ctl 方法,用来维护 epoll 所监控的所有 socket,如果要新加一个 socket 或者删除一个 socket,调用该方法即可。它会将维护的这个 socket 集合映射到内核中,就不用每次都复制了。

  • socket 唤醒 select 时,不能告诉它具体是哪个 socket 发生了事件。引入了一个 ready_list 双向链表,会将发生事件的 socket 加入到这个链表中,那么唤醒 epoll 时,后者就会直到哪些 socket 发生了事件。遍历 ready_list 处理事件是,有两种模式:

    • ET:边缘触发,遍历 ready_list 时,在读取 socket 中的事件之后会把 socket 从 ready_list 中移除。
    • LT:水平触发,读取 socket 中的事件之后,如果这个 socket 返回了感兴趣的事件,就不会把它删除。

    比如有一个客户端同时发来了 5 个数据包,按正常逻辑,这个 socket 只会往 ready_list 中加入一次,用户程序拿到这个 socket 之后,把其中的 5 个数据包都读完即可。如果读取第一个包时发生了异常,那么 —— 如果是 ET 模式,那么后面的 4 个数据包就读不了了,因为 socket 已经被移除了;而在 LT 模式下,因为 socket 发生了感兴趣的事件,所以这个 socket 不会被删除,后面的 4 个数据包还是可以正常访问。

Reactor 线程模型

传统阻塞 IO 模型

  • 线程数量可能过大:请求和处理线程一一对应,每收到一个连接请求就需要用一个线程去处理。所以当并发量很大时,就会因为创建了大量线程而占用过多资源。
  • 阻塞:且连接创建后,如果当前线程暂时没有数据可读,线程就会阻塞在 read 操作,浪费线程资源。

Reactor 模式

针对阻塞 IO 模型的两个缺点,Reactor 模式提出了对应的解决方案:

  • 使用线程池,将连接完成后的业务处理任务提交到线程池即可,无需为每个连接创建线程。
  • 采用 IO 多路复用,多个连接共用一个阻塞对象,当其中某个连接有新的数据时,线程就会被唤醒。

Reactor 模式也叫 Dispatcher 模式,收到连接请求后,通过 eventDispatcher 分派给事件处理器。

Reactor 负责 监听和分发事件

Reactor 模式根据 Reactor 数量处理线程数量 的不同,可以分成三种类型:

  • 单 Reactor 单线程
  • 单 Reactor 多线程
  • 多 Reactor 多线程

1️⃣ 单 Reactor 单线程

Reactor 通过 select 监听客户端请求事件(select 可以通过一个阻塞对象监听多路连接请求),收到事件后通过 dispatch 进行分派:

  • 如果是连接请求,则由 Acceptor 通过 accept 命令建立连接,然后创建一个 Handler 处理后续业务;
  • 如果不是连接请求,则分派给对应的 Handler 来处理。Handler 会完成 read → 业务已处理 → send 这个完成流程。

适用于客户端数量有限的情况,如果客户端连接较多,会处理不过来。

2️⃣ 单 Reactor 多线程

Reactor 依旧通过 select 监听客户端请求事件,通过 dispatch 进行分派,但是在 Handler 中使用线程池去处理实际的业务。

  • 如果收到连接请求,就分派给 Acceptor,后者通过 accept 命令建立连接,然后创建 Handler 处理后续请求;
  • 如果不是连接请求,就分派给对应的 Handler 去处理。Handler 不再处理实际的业务,而只是负责响应将实际的业务处理交给线程池。

可以充分利用 CPU 资源,单仍然使用单个 Reactor 线程,在高并发场景下容易出现性能瓶颈。

3️⃣ 多 Reactor 多线程

主 Reactor 负责创建新的连接,连接创建后,会把这个连接交给子 Reactor 来负责。 具体过程为:

  1. 主 Reactor 通过 select 监听请求事件,如果是连接请求,则分派给 Acceptor 去处理,建立连接后,主 Reactor 将连接分配给子 Reactor
  2. 子 Reactor 将连接放入连接队列进行监听,并为每个连接创建 Handler 来处理业务。有新事件发生时,子 Reactor 调用对应 Handler 来处理。同样地,Handler 只是负责响应,具体的业务处理交给线程池。

主 Reactor 可以创建多个子 Reactor,一个 Reactor 处理多个连接(都放在连接队列中)。

标签:02,面试题,Java,Reactor,线程,IO,连接,select,socket
From: https://www.cnblogs.com/lzh1995/p/16758032.html

相关文章

  • JavaScript_大文件切片上传
    bigfile-chunk-upload功能大文件截取分块上传,带请求并发控制、错误重发功能。教程(以Vue为例)下载npminstallbigfile-chunk-upload引入//page.vueimportbigFile......
  • Java 面试题 01 - Java 基础
    基础概念JDK、JRE、JVM的区别?JDK是Java开发工具包,包含了Java的开发工具(编译工具javac.exe和打包工具jar.exe等)和JRE。JRE是Java运行环境,提供了库、JVM......
  • ECCV2020 | Unsupervised Batch Normalization
    计算机视觉研究院专栏作者:Edison_GBN的基本思想:因为深层神经网络在做非线性变换前的激活输入值(就是x=WU+B,U是输入)随着网络深度加深或者在训练过程中,其分布逐渐发生偏移或者......
  • DIY(02)——根据现有名单从原始文件中复制名单文件进入新的文件夹
    涉及到内容:1.获取文件名称,并根据名称与名单进行比对2.拷贝文件  %filenameisdeliverdataclc;clearall;closeall;%读取文件名fileFolder=fullfile('D:\z')......
  • java--while小练习和switch小练习
    while小练习动态录入学生个数,成绩,求总成绩和平均值packagelearnday2;importjava.util.Scanner;*@description:动态录入学生成绩,并求总分和平均值publicclasswhileDe......
  • java课后反思
        在定义变量时,我们需要对变量进行初始化才可以继续进行使用,同时,对象变量如果不引用一个真实的值,则必须·对他声明为null;  对于原始数据类型变量,可以使用==......
  • 备战2021:vue3+ts开发指南
    Vue3+Typescript开发指南为什么要使用Ts应不应该使用TS开发Vue3是当前的热门话题,大家主要纠结成本和收益之间的取舍。什么是TypeScript官网:构建于JavaScript,增加了静态类......
  • 备战2021:vite2项目最佳实践
    备战2021:Vite2项目最佳实践作者同款机械键盘vite2来了​​Vite1​​​还没用上,​​Vite2​​​已经更新了,全新插件架构,丝滑的开发体验,和​​Vue3​​的完美结合。2021年第一......
  • 备战2021:Vite2插件开发指南
    Vite插件是什么使用Vite插件可以扩展Vite能力,比如解析用户自定义的文件输入,在打包代码前转译代码,或者查找第三方模块。image-20210216214524914Vite插件的形式​​Vite​​......
  • 2022.10.6java分支结构
    HelloWorld打开idea,新建java文件,新建javaclass编写代码psvm自动生成publicstaticvoidmain(Stringsargs{}sout自动生成System.out.printlnpublicclass......