IO多路复用和线程池哪个效率更高,更有优势

io多路复用(这翻译真的很坑爹啊),指的是同一个进(线)程可以处理多个IO数据流。
多线程+池模型指的是每个线程处理一个IO流。

O多路复用的优势在于,当处理的消耗对比IO几乎可以忽略不计时,可以处理大量的并发IO,而不用消耗太多CPU/内存。这就像是一个工作很高效的人,手上一个todo list,他高效的依次处理每个任务。这比每个任务单独安排一个人要节省(雇人是要发工资的……)。典型的例子是nginx做代理,代理的转发逻辑相对比较简单直接,那么IO多路复用很适合。相反,如果是一个做复杂计算的场景,计算本身可能是个 指数复杂度的东西,IO不是瓶颈。那么怎么充分利用CPU或者显卡的核心多干活才是关键。此外,IO多路复用适合处理很多闲置的IO,因为IO socket的数量的增加并不会带来进(线)程数的增加,也就不会带来stack内存,内核对象,切换时间的损耗。因此像长链接做通知的场景非常适合。IO多路复用 + 单进(线)程有个额外的好处,就不会有并发编程的各种坑问题,比如在nginx里,redis里,编程实现都会很简单很多。编程中处理并发冲突和一致性,原子性问题真的是很难,极易出错。

如果做不到“处理过程相对于IO可以忽略不计”,IO多路复用的并不一定比线程池方案更好。比如一个web的服务,用jetty 9的NIO connector,后边是spring svc + JDBC连接数据库。spring svc + JDBC连接数据库这两块的处理延迟相对于NIO来说不能忽略,所以并不能指望用jetty 9的NIO connector换了之前的BIO connector的容器,性能能高不少(实际上应该会高一些,但不会太夸张,毕竟瓶颈在后边处理和DB上)。
顺便提一句,Java世界里,因为JDBC这个东西是BIO的,所以在我们常见的Java服务里没有办法做到全部NIO化,必须得弄成多线程模型。如果要在做Java web服务这个大场景下享受IO多路复用的好处,要不就是不需要DB的,要不就是得用Vert.X一类的纯NIO框架把DB IO访问也得框进来。最后,如果IO压力过大,一个高并发的东西和一个不那么高并发的东西,都不能正确响应,对用户来说是一样的——就是不能用。假如IO非常的繁重,没有空闲的连接,那么IO的压力在两种模型下表现差不多,IO多路复用的“并发“看了起来会大一些,但因为IO已经满了,所以表现出超时严重;而线程池可能表现为,所有的线程都因为IO过慢而卡死了,线程池耗尽,新的请求进不来直接报错。但不管哪一种,在极端压力下,都无法正常工作。这时,要想着怎么扩容。

简单总结一下

如果压力不是很大,并且处理性能相对于IO可以忽略不计
IO多路复用+单进(线)程比较省资源适合处理大量的闲置的IO
IO多路复用+多单进(线)程与线程池方案相比有好处,但是并不会有太大的优势如果压力很大,什么方案都得跪,这时就得扩容。当然因为IO多路复用+单进(线)程比较省资源,所以扩容时能省钱。

redis的线程

redis是单线程操作的,但是却可以处理高并发。原因是基于多路复用的非阻塞IO,基于NIO(non_blocking_io);

redis为什么这么快?

完全基于内存,绝大部分请求是纯粹的内存操作;
数据结构简单,对数据操作也简单,redis中的数据结构是专门进行设计的;
采用单线程,避免了不必要的上下文切换和竞争条件,不用考虑加锁释放锁和死锁的问题;

对多路复用的理解

多路Io复用是利用select、poll、epoll可以同时监察多个流的IO事件的能力,在空闲的时候会把当前线程阻塞,当有一个或多个流由IO事件发生时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll只是轮询那些真正发出了时间的流),并且一次顺序的处理就绪的流。

  • select:服务端一直在轮询、监听如果有客户端链接上来就创建一个连接放到数组A中,继续轮询这个数组,如果在轮询的过程中有客户端发生IO事件就去处理;select只能监视1024个连接(一个进程只能创建1024个文件);而且存在线程安全问题;
  • poll:在select做了许多修复,比如不限制监测的连接数;但是也有线程安全问题;
  • epoll:也是监测IO事件,但是如果发生Io事件,它会告诉你是哪个连接发生了事件,就不用再轮询访问。而且它是线程安全的,但是只有linux平台支持;

参考:https://blog.csdn.net/mashaokang1314/article/details/88636371

IO多路复用(Reactor),事件驱动

在这里插入图片描述

在这里插入图片描述

对BIO、NIO和AIO的概念解析

所有的系统IO都分为两个阶段:等待就绪和操作。例如:读函数分为等待系统可读和真正的读;同理,写函数分为等待网卡可写和真正的写。

可以将这三种IO的特点概括为:
BIO里用户最关心"我要读";我要一直等在这处理处理时间;
NIO里用户最关心"我可以读了";我不在这等了,如果有事件发生,你就通知我,我再来处理;
AIO里用户最关心"读完了";你帮我处理吧,处理完了通知我。

BIO

Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
每一个线程守着一个IO通道,当没有IO时这个线程就阻塞着,一旦有IO事件发生,这个线程就开始工作

  • 缺点
    严重依赖线程:
    线程的创建和销毁成本很高;
    线程本身占用较大内存;
    线程切换成本很高;
    容易造成锯齿状的系统负载;
Java NIO

同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。socket主要的读、写、注册和接受函数,在等待就绪阶段都是非阻塞的,真正的IO操作是同步阻塞的。
将每个连接(IO通道)都注册到Selector多路复用器上,告诉复用器我已经连接了,如果有IO事件发生,你就通知我。Selector就不断地调用select函数,去访问每一个通道看那个通道有IO事件发生,如果发生了就通知,内核开启一个对应事件的线程去工作

Java AIO(NIO.2)

异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。 即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。

  • 10
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
IO多路复用和多线程是两种处理并发连接的方式,它们有以下区别: 1. 并发连接处理方式:IO多路复用是通过一个线程管理多个连接,而多线程是为每个连接创建一个独立的线程。 2. 系统调用次数:IO多路复用使用一个系统调用来等待多个连接的就绪状态,而多线程需要为每个连接执行系统调用。 3. 内存占用:IO多路复用只需要一个线程来管理多个连接,因此内存占用相对较低;而多线程需要为每个连接创建一个独立的线程,可能导致较高的内存占用。 4. 上下文切换开销:IO多路复用只需要一个线程进行上下文切换,而多线程需要频繁进行线程之间的上下文切换,可能导致较高的开销。 5. 编程复杂性:IO多路复用相对于多线程来说,编程复杂性较高,需要使用特定的API来管理并发连接;而多线程相对简单直观,但需要注意线程安全问题。 6. 可扩展性:IO多路复用在处理大量并发连接时具有较好的可扩展性,因为它只需要一个线程来管理所有连接;而多线程在处理大量并发连接时,线程数量的增加可能会导致系统资源耗尽。 综上所述,IO多路复用适用于连接数较多,但每个连接的处理逻辑简单的场景,而多线程适用于连接数较少,但每个连接的处理逻辑复杂的场景。选择哪种方式应根据具体需求和系统资源情况进行综合考虑。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值