关于后端:java-5种IO模型

2次阅读

共计 3359 个字符,预计需要花费 9 分钟才能阅读完成。

每日一句

人的苦楚会把本人折磨到多深呢?

每日一句

You cannot swim for new horizons until you have courage to lose sight of the shore.
除非有勇气来到岸边,否则你永远游不到此岸。

概念

IO 是主存和外部设备(硬盘、终端和网络等)拷贝数据的过程。IO 是操作系统的底层性能实现,底层通过 I / O 指令进行实现。

以下是 5 品种 Unix 下可用的 I / O 模型

1. 阻塞式 I /O:Blocking IO

2. 非阻塞式 I /O:nonblocking IO

3. I/O 复用(Select,poll epoll):IO multiplexing

4. 信号驱动式 I /O(SIGIO):signal driven IO

5. 异步 I/O(posix 的 aio 系列函数):asynchromous IO

Blocking IO

在 Linux 中,默认状况下所有的 socket 都是 Blocking,一个典型的读操作流程大略是这样:

1. 通常波及期待数据从网络达到。当所有期待数据达到时,它被复制到内核中的某个缓冲区

2. 把数据从内核缓冲区复制到应用程序缓冲区

当用户过程调用了 recvfrom 这个零碎调用,kernel 就开始了 IO 的第一个阶段:筹备数据。对于 network IO 来说,很多时候数据在一开始还没有达到(比方,还没有收到一个残缺的 UDP 包)。这个时候 kernel 就要期待足够的数据到来。而在用户过程这边,整个过程会被阻塞。当 kernel 始终等到数据筹备好了,它就会将数据从 kernel 中拷贝到用户内存,而后 kernel 返回后果,用户过程才解除 block 的状态,从新运行起来。
所以,Blocking IO 的特点就是在 IO 执行的两个阶段都被 block 了

非阻塞式 I /O

Linux 下,能够用过设置 socket 使其变为 non-blocking。当对一个 non-blocking socket 执行操作时,流程如下:
从图中能够看出,当用户过程收回 read 操作时,如果 kernel 中的数据还没有筹备好,那么它并不会 block 用户过程,而是立即返回一个 error。从用户过程角度讲,它发动一个 read 操作后,并不需要期待,而是马上就失去了一个后果。用户过程判断后果是一个 error 时,它就晓得数据还没有筹备好,于是它能够再次 发送 read 操作。一旦 kernel 中的数据筹备好了,并且又再次收到了用户过程的 system call,那么它马上就将数据拷贝到了用户内存,而后返回。

所以,用户过程第一个阶段不是阻塞的, 须要一直的被动询问 kernel 数据好了没有;第二个阶段仍然总是阻塞的。

IO 多路复用(NIO)

select、epoll 的益处就在于单个 process 就能够同时解决多个网络链接的 IO。

IO 复用和同步阻塞实质一样,不过利用了新的 Select 零碎调用,由内核来负责原本是申请过程该做的轮训操作,看似不非阻塞 IO 还多了哥零碎调用开销,不过因为反对多路 IO 才算进步了效率
也就是一个能够监听多个。
它的基本原理就是 select、epoll 这个 function 会一直的轮询所负责的所有 socket,当某个 socket 有数据达到了,就告诉用户过程。它的流程如下图:

当用户线程调用 select,那么整个过程会被阻塞,而同时,kernel 会监督所有 select 负责的 socket =,当任何一个 socket 中的数据筹备好了,select 就会返回,这个时候用户过程会调用 read 操作,将数据 kernel 拷贝到用户过程。
首先开启套接字的信号驱动式 IO 性能,并且通过 sigaction(信号处理程序) 零碎调用装置一个信号处理函数,该函数调用将立刻返回,以后过程没有被阻塞,持续工作;当数据报筹备好的时候,内核为该过程产生 SIGIO 的信号,随后既能够在信号处理函数中调用 recvfrom 读取数据报,并且告诉主循环数据曾经筹备好期待解决;也能够间接告诉主循环让它读取数据报;(其实就是一个待读取的告诉和待处理的告诉),根本不会用到。

异步 IO(AIO)

多线程和多过程的模型尽管解决了并发的问题,然而零碎不能有限的减少线程,因为零碎的切换线程的开销恒大,所以,一旦线程数量过多,CPU 的工夫就花在线程的切换上,正真运行代码的工夫就会缩小,后果导致性能重大降落

因为咱们要解决的问题是 CPU 高速执行能力和 IO 设施的龟速重大不匹配,多线程和多过程只是解决这一个问题的一种办法。
另一种解决 IO 问题的办法是异步 IO,当代码须要执行一个耗时的 IO 操作时,他只收回 IO 指令,并不期待 IO 后果而后就去执行其余代码,一段时间后,当 IO 返回后果是,在告诉 CPU 进行解决咱们调用 aio_read 函数,给内核传递描述符,缓冲区指针,缓冲区大小,和文件偏移量,并且通知内核当整个操作实现时如何告诉咱们,该函数调用后,立刻返回,不会被阻塞

另一方面:从 kernel 的角度,当他收到一个 aio_read 之后,首先它立刻返回,所以不会对用户过程产生 block,而后 kernel 会期待数据筹备实现,而后将数据拷贝到用户内存(copy 由内核实现),当着所有实现后,kernel 会给用户过程发送一个 singal 或者执行下一个基于线程回调函数来实现此次 IO 处理过程,通知他 read 操作实现

美文佳句

岁月应是静好。近来,心神总是不宁,想必是被尘世间的引诱所困。

我想,要是远方的祥夫学生来到我这“梅知堂”,两人清茶一杯,盘腿而坐,或看他画梅,纸上的梅花好像兀自开在飞雪里,如是,那该是一幅怎么的墨梅图?

昨晚,梦里看到那个戴着小黑圆眼镜的祥夫背着黄色的大包正往江南赶路。是又到了梅花开的日子了吗?

面试题

SpringMVC 工作原理?

1. 客户端发送申请到前端控制器 DispatcherServlet
2. DispatcherServlet 收到申请后,调用 HandlerMapping 处理器映射器,申请获取 handler
3. 处理器映射器依据 url 找到具体的处理器,生成处理器对象以及处理器拦截器(如果有则生成)一并返回给 DispatcherServlet
4. DispatcherServlet 调用 HandlerAdapter 处理器适配器
5. HandlerAdapter 通过适配器调用 具体处理器(Handler,也叫后端控制器)6. Handler 执行实现返回 ModelAndView
7. HandlerAdaper 将 Handler 执行后果 ModelAndView 返回给 DispatcherServlet
8. DispatcherServlet 将 ModelAndView 传给 ViewResolver 视图解析器 进行解析
9. ViewResolver 解析后返回具体 View
10. DispatcherServlet 对 view 进行 渲染视图(行将模型数据填充至视图中)11. DispatcherServlet 响应用户

ArrayList、LinkedList 和 Vector 的区别?

ArrayList LinkedList Vector
线程是否平安 不保障 不保障 保障线程平安,然而底层大量应用了 synchronized 关键字,效率不是很高
底层数据结构 数组,查问效率高,多用于查问较多的场合 LinkedList 底层是双向链表,插入和删除十分不便,实用于插入较多的场合 数组,查问效率高,多用于查问较多的场合
默认大小与扩容 JDK 1.7 之前默认大小是 10 JDK 1.7 之后是 0 每次依照 1.5 倍扩容 底层是链表构造,是不间断的存储空间,没有默认大小的说法 Vector 扩容是 2 倍

@RestController 和 @Controller 有什么区别?

@RestController 注解,在 @Controller 根底上,减少了 @ResponseBody 注解,更加适宜目前前后端拆散的架构下,提供 Restful API,返回例如 JSON 数据格式。当然,返回什么样的数据格式,依据客户端的 "ACCEPT" 申请头来决定。

你好,我是 yltrcc,日常分享技术点滴,欢送关注我:ylcoder

正文完
 0