多线程与消息队列

多线程

  • 通常多线程的应用不是为了提高运行效率,而是为了提高资源使用效率(单核CPU不行,反而降低),还可以实现异步调用。
  • 单核CPU同一时间只能处理一个线程(因为一个CPU一次只能执行一条指令),但速度非常快,消除阻塞,造成并行的假象(并发:交替轮流使用资源)
  • 多核CPU同一时间可以处理多个线程,每个核心处理一个线程。(并行:分工同时处理多个任务)

创建线程池的5种方式

  • 线程池(ThreadPool)是一种基于池化思想管理和使用线程的机制。它是将多个线程预先存储在一个“池子”内,当有任务出现时可以避免重新创建和销毁线程所带来性能开销,只需要从“池子”内取出相应的线程执行对应的任务即可。

  • 一、通过Executors类提供的4种方法:

    1. newCachedThreadPool     //(创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。)
    2. newFixedThreadPool      //(创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。)
    3. newScheduledThreadPool  //(创建一个定长线程池,支持定时及周期性任务执行。)
    4. newSingleThreadExecutor //(创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。)
    
  • 二、通过ThreadPoolExecutor类自定义:

    5. ThreadPoolExecutor 类提供了4种构造方法,可根据需要来自定义一个线程池。
    

ThreadPoolExecutor线程池工作原理

主要参数为:核心线程数、阻塞队列、最大线程数、拒绝策略。
image

四种拒绝策略:

策略 处理方式
AbortPolicy 直接抛出异常
DiscardPolicy 丢弃当前被拒绝的任务(不抛出异常)
DiscardOldestPolicy 将工作队列中最早的任务丢弃,去执行新的任务
CallerRunsPolicy 交给调用线程池的线程处理

消息队列

  • 消息(Message)是指应用于应用之间传送的数据,消息的类型包括文本字符串、JSON、XML、内嵌对象等等...

  • 所谓 消息中间件 / 消息队列(Message Queue Middleware,简称MQ)是利用高效可靠的消息传递机制进行数据交流,同时可以基于数据通信来进行分布式系统的继承,消息中间件一般有两种传递模式:点对点(Point-to-Point)模式和发布/订阅(Pub/Sub)模式,点对点模式是基于队列的,消息生产者发送消息到队列,消息消费者从队列中接收消息,队列的存在使得消息的异步传输成为了可能,发布订阅模式定义了如何向一个内容节点发布和订阅内容,这个内容节点叫topic,这种模式可以满足消费者发布一个消息,多个消费者同时消费同一信息的需求。

  • 一般来说,消息队列是一种异步的服务间通信方式,是分布式系统中重要的组件,主要解决应用耦合,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。使用较多的消息队列有ActiveMQ、RocketMQ、RabbitMQ、Kafka等。

消息队列

多线程和消息队列的区别?

  • 多线程是防止系统的阻塞(优先响应用户,后台任务执行)
  • 消息队列是提高系统处理业务的效率(异步处理加快程序执行速度)

消息队列和多线程的选择

  1. 可靠性要求高时选择消息队列:消息队列和多线程两者并不冲突,多线程可以作为队列的生产者和消费者。使用外部的消息队列时,第一是可以提高应用的稳定性,当程序fail后,已经写入外部消息队列的数据依旧是保存的,如果使用两步commit的队列的话,可以更加提高这个项目。

  2. 不着急知道结果,尽量使用消息队列,保证服务器的压力减小,因为多线程对cpu的消耗大一点:用线程的话,会占用主服务器资源, 消息队列的话, 可以放到其他机器上运行, 让主服务器尽量多的服务其他请求。我个人认为, 如果用户不急着知道结果的操作, 用消息队列, 否则再考虑用不用线程。

  3. 需要解耦的时候用消息队列:解耦更充分,架构更合理。多线程是在编程语言层面解决问题,消息队列是在架构层面解决问题。我认为架构层面解决问题是“觉悟比较高的方式“,理想情况下应该限制语言层面滥用多线程,能不用就不用。

  4. 如果容易出现线程安全问题的业务或者批量操作时,也尽量使用消息队列:批量发送邮件时,数据量庞大,如果使用多线程对系统不安全。

消息队列和线程池的比较

  1. 两者内部都使用了队列,如阻塞队列、优先级队列;

  2. 使用线程池时应用服务器既充当生产者又充当消费者,也是消息队列中间件的实现者,使用消息队列时中间件、生产者、消费者可以部署在不同的应用机器上(当然也可以部署在一台服务器上但很少有人这么用);

  3. 出于第2点线程池更适合非分布式的系统,但在分布式架构下消息队列明显是更突出优势;

  4. 使用消息队列会带来额外的网络开销;

  5. 消息队列的耦合性更低,可扩展性更好,适用于弱一致性的场景,如对log日志的解耦;

  6. 消息队列自动实现消息的持久化,中间已经实现了大量功能,如消息转发、消息拒绝、消息重试,以及对消息的一些监控,例如消息的消费状态、消息的消费速率等,使用线程池如果需要很多功能还要自己去实现,例如需要执行状态需要打印队列数量、计算消息消费速度;

  7. 在不同系统间的服务调用(调用协议也可能不一致)线程池很难实现或开销很大,这时候消息队列可以屏蔽不同机器或不同协议的问题;

  8. 使用消息队列会提升系统的复杂度,网络抖动怎么办?最大队列长度怎么设置?超时时间又设置多少?Qos又设置为多少?消费者多少个比较合适?Channel cache size又该设置为多少?业务线可能都是用同一个Mq,你占资源太多,或者设计不当可能会导致整个Mq故障。

posted @ 2022-05-11 09:08  zhαojh  阅读(2451)  评论(0编辑  收藏  举报