RocketMQ 是一款开源的分布式音讯体系,根据高可用分布式集群技能,供给低延时、高牢靠的音讯发布与订阅服务。

这篇文章,笔者整理了 RocketMQ 源码中创立线程的几点技巧,希望咱们读完之后,可以有所收获。

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

1 创立单线程

首要咱们先温习下常用的创立单线程的两种办法:

  • 完成 Runnable 接口
  • 承继 Thread 类

▍一、完成 Runnable 接口

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

图中,MyRunnable 类完成了 Runnable 接口的 run 办法,run 办法中界说具体的使命代码或处理逻辑,而Runnable 对象是作为线程构造函数的参数。

▍二、 承继 Thread 类

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

线程完成类直接承继 Thread ,本质上也是完成 Runnable 接口的 run 办法。

2 单线程抽象类

创立单线程的两种办法都很简略,但每次创立线程代码显得有点冗余,所以 RocketMQ 里完成了一个抽象类 ServiceThread 。

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

咱们可以看到抽象类中包括了如下中心办法:

  1. 界说线程名;
  2. 发动线程;
  3. 关闭线程。

下图展示了 RocketMQ 很多的单线程完成类。

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

完成类的编程模版相似 :

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

咱们仅仅需要承继抽象类,并完成 getServiceNamerun 办法即可。发动的时候,调用 start 办法 , 关闭的时候调用 shutdown 办法。

3 线程池原理

线程池是一种根据池化思想办理线程的东西,线程池维护着多个线程,等待着监督办理者分配可并发履行的使命。这避免了在处理短时刻使命时创立与毁掉线程的价值。线程池不只可以确保内核的充分利用,还能防止过分调度。

JDK中供给的 ThreadPoolExecutor 类,是咱们最常运用的线程池类。

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

参数名 效果
corePoolSize 行列没满时,线程最大并发数
maximumPoolSizes 行列满后线程可以到达的最大并发数
keepAliveTime 闲暇线程过多久被收回的时刻约束
unit keepAliveTime 的时刻单位
workQueue 堵塞的行列类型
threadPoolFactory 改变线程的称号、线程组、优先级、看护进程状态
RejectedExecutionHandler 超出 maximumPoolSizes + workQueue 时,使命会交给RejectedExecutionHandler来处理

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

使命的调度经过履行 execute办法完成,办法的中心流程如下:

  1. 如果 workerCount < corePoolSize,创立并发动一个线程来履行新提交的使命。
  2. 如果 workerCount >= corePoolSize,且线程池内的堵塞行列未满,则将使命添加到该堵塞行列中。
  3. 如果 workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的堵塞行列已满,则创立并发动一个线程来履行新提交的使命。
  4. 如果 workerCount >= maximumPoolSize,并且线程池内的堵塞行列已满, 则根据拒绝战略来处理该使命, 默许的处理办法是直接抛异常。

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

4 线程池封装

在 RocketMQ 里 ,网络请求都会携带指令编码,每种指令映射对应的处理器,而处理器又会注册对应的线程池。

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

当服务端 Broker 接收到发送音讯指令时,都会有独自的线程池 sendMessageExecutor 来处理这种指令请求。

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

根据 ThreadPoolExecutor 做了一个简略的封装 ,BrokerFixedThreadPoolExecutor 构造函数包括六个中心参数:

  1. 中心线程数和最大线程数相同 ,数量是:cpu核数和4比较后的最小值;
  2. 闲暇线程的收回的时刻约束,默许1分钟;
  3. 发送音讯行列,有界行列,默许10000;
  4. 线程工厂 ThreadFactoryImpl ,界说了线程名前缀:SendMessageThread_ 。

RocketMQ 完成了一个简略的线程工厂:ThreadFactoryImpl,线程工厂可以界说线程称号,以及是否是看护线程 。

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

开源项目 Cobar ,Xmemcached,Metamorphosis 中都有相似线程工厂的完成 。

5 线程名很重要

线程名很重要,线程名很重要,线程名很重要 ,重要的事情说三遍。

咱们看到 RocketMQ 中,无论是单线程抽象类还是多线程的封装都会装备线程名 ,由于经过线程名,非常容易定位问题,从而大大提高解决问题的功率

定位的前言常见有两种:日志文件仓库记载

▍一、日志文件

常常处理事务问题的同学,一定都常常与日志打交道。

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

  • 检查 ERROR 日志,追溯到履行线程, 要是线程池阻隔做的好,根本可以判断出哪种事务场景出了问题;
  • 经过检查线程打印的日志,揣度线程调度是否正常,比方有的定时使命线程打印了开端,没有打印结束,推论当时线程可能现已挂掉或许堵塞。

▍二、仓库记载

jstack 是 java 虚拟机自带的一种仓库跟踪东西 ,主要用来检查 Java 线程的调用仓库,线程快照包括当时 java 虚拟机内每一条线程正在履行的办法仓库的集合,可以用来剖析线程问题。

jstack -l 进程pid

读完 RocketMQ 源码,我学会了怎么高雅的创立线程

笔者检查线程仓库,一般关注如下几点:

  1. 当时 jvm 进程中的线程数量和线程分类是否在预期的范围内;
  2. 体系接口超时或许定时使命停止的异常场景下 ,剖析仓库中是否有锁未开释,或许线程一向等待网络通讯呼应;
  3. 剖析 jvm 进程中哪个线程占用的 CPU 最高。

6 总结

本文是RocketMQ 系列文章的开篇,和朋友们简略聊聊 RocketMQ 源码里创立线程的技巧。

  1. 单线程抽象类 ServiceThread

    运用者只需要完成事务逻辑以及界说线程名即可 ,不需要写冗余的代码。

  2. 线程池封装

    适当封装,界说线程工厂,并合理装备线程池参数。

  3. 线程名很重要

    文件日志,仓库记载配合线程名能大大提高解决问题的功率。

RocketMQ 的多线程编程技巧很多,比方线程通讯,并发控制,线程模型等等,后续的文章会一一为咱们展示。


如果我的文章对你有所协助,还请帮助点赞、在看、转发一下,你的支撑会鼓励我输出更高质量的文章,非常感谢!