读完 RocketMQ 源码,我学会了怎样优雅的创建线程

开发者 2024-9-19 12:11:17 63 0 来自 中国
RocketMQ 是一款开源的分布式消息体系,基于高可用分布式集群技能,提供低延时、高可靠的消息发布与订阅服务。
这篇文章,笔者整理了 RocketMQ 源码中创建线程的几点本事,盼望各人读完之后,可以大概有所劳绩。
1.png 1 创建单线程

起首我们先温习下常用的创建单线程的两种方式:

  • 实现 Runnable 接口
  • 继续 Thread 类
▍一、实现 Runnable 接口
图中,MyRunnable 类实现了 Runnable 接口的 run 方法,run 方法中界说详细的任务代码或处理处罚逻辑,而Runnable 对象是作为线程构造函数的参数。
▍二、 继续 Thread 类
3.png 线程实现类直接继续 Thread ,本质上也是实现 Runnable 接口的 run 方法。
2 单线程抽象类

创建单线程的两种方式都很简单,但每次创建线程代码显得有点冗余,于是 RocketMQ 里实现了一个抽象类 ServiceThread 。
我们可以看到抽象类中包罗了如下核心方法:

  • 界说线程名;
  • 启动线程;
  • 关闭线程。
下图展示了 RocketMQ 浩繁的单线程实现类。
5.png 实现类的编程模版雷同 :
6.png 我们仅仅必要继续抽象类,并实现 getServiceNamerun 方法即可。启动的时候,调用 start 方法 , 关闭的时候调用 shutdown 方法。
3 线程池原理

线程池是一种基于池化头脑管理线程的工具,线程池维护着多个线程,期待着监视管理者分配可并发执行的任务。这制止了在处理处罚短时间任务时创建与烧毁线程的代价。线程池不光可以大概包管内核的充实利用,还能防止太过调治。
JDK中提供的 ThreadPoolExecutor 类,是我们最常利用的线程池类。
7.png 参数名作用corePoolSize队列没满时,线程最大并发数maximumPoolSizes队列满后线程可以大概到达的最大并发数keepAliveTime空闲线程过多久被采取的时间限定unitkeepAliveTime 的时间单元workQueue壅闭的队列范例threadPoolFactory改变线程的名称、线程组、优先级、守卫历程状态RejectedExecutionHandler超出 maximumPoolSizes + workQueue 时,任务会交给RejectedExecutionHandler来处理处罚 8.png 任务的调治通过执行 execute方法完成,方法的核心流程如下:

  • 假如 workerCount < corePoolSize,创建并启动一个线程来执行新提交的任务。
  • 假如 workerCount >= corePoolSize,且线程池内的壅闭队列未满,则将任务添加到该壅闭队列中。
  • 假如 workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的壅闭队列已满,则创建并启动一个线程来执行新提交的任务。
  • 假如 workerCount >= maximumPoolSize,而且线程池内的壅闭队列已满, 则根据拒绝计谋来处理处罚该任务, 默认的处理处罚方式是直接抛非常。
4 线程池封装

在 RocketMQ 里 ,网络请求都会携带下令编码,每种下令映射对应的处理处罚器,而处理处罚器又会注册对应的线程池。
10.png 当服务端 Broker 吸取到发送消息下令时,都会有单独的线程池 sendMessageExecutor 来处理处罚这种下令请求。
基于 ThreadPoolExecutor 做了一个简单的封装 ,BrokerFixedThreadPoolExecutor 构造函数包罗六个核心参数:

  • 核心线程数和最大线程数雷同 ,数目是:cpu核数和4比较后的最小值;
  • 空闲线程的采取的时间限定,默认1分钟;
  • 发送消息队列,有界队列,默认10000;
  • 线程工厂 ThreadFactoryImpl ,界说了线程名前缀:SendMessageThread_ 。
RocketMQ 实现了一个简单的线程工厂:ThreadFactoryImpl,线程工厂可以界说线程名称,以及是否是守卫线程 。
12.png
开源项目 Cobar ,Xmemcached,Metamorphosis 中都有雷同线程工厂的实现 。
5 线程名很紧张

线程名很紧张,线程名很紧张,线程名很紧张 ,紧张的事变说三遍。
我们看到 RocketMQ 中,无论是单线程抽象类照旧多线程的封装都会设置线程名 ,由于通过线程名,非常容易定位标题,从而大大提升办理标题的服从。
定位的媒介常见有两种:日记文件堆栈记录
▍一、日记文件
常常处理处罚业务标题的同砚,肯定都常常与日记打交道。


  • 查察 ERROR 日记,追溯到执行线程, 要是线程池隔离做的好,根本可以判定出哪种业务场景出了标题;
  • 通过查察线程打印的日记,推断线程调治是否正常,比如有的定时任务线程打印了开始,没有打印竣事,推论当前线程大概已经挂掉大概壅闭。
▍二、堆栈记录
jstack 是 java 假造机自带的一种堆栈跟踪工具 ,重要用来查察 Java 线程的调用堆栈,线程快照包罗当前 java 假造机内每一条线程正在执行的方法堆栈的聚集,可以用来分析线程标题。
jstack -l 历程pid笔者查察线程堆栈,一样平常关注如下几点:

  • 当前 jvm 历程中的线程数目和线程分类是否在预期的范围内;
  • 体系接口超时大概定时任务制止的非常场景下 ,分析堆栈中是否有锁未开释,大概线程不停期待网络通讯相应;
  • 分析 jvm 历程中哪个线程占用的 CPU 最高。
6 总结

本文是RocketMQ 系列文章的开篇,和朋侪们简单聊聊 RocketMQ 源码里创建线程的本事。

  • 单线程抽象类 ServiceThread
    利用者只必要实现业务逻辑以及界说线程名即可 ,不必要写冗余的代码。
  • 线程池封装
    得当封装,界说线程工厂,并公道设置线程池参数。
  • 线程名很紧张
    文件日记,堆栈记录共同线程名能大大提升办理标题的服从。
RocketMQ 的多线程编程本事很多,比如线程通讯,并发控制,线程模子等等,后续的文章会逐一为各人显现。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-22 11:40, Processed in 0.183869 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表