看完RocketMQ源码,学会了如何优雅的创建线程。RocketMQ是一个开源的分布式消息系统,基于高可用的分布式集群技术,提供低延迟、高可靠的消息发布和订阅服务。在这篇文章中,笔者整理了一些在RocketMQ源码中创建线程的技巧。希望大家看完后都能有所收获。1创建单线程首先我们回顾一下创建单线程常用的两种方法:实现Runnable接口和继承Thread类▍1.实现Runnable接口图中MyRunnable类实现了Runnable的run方法接口,run方法定义了具体的Task代码或处理逻辑,而Runnable对象作为线程构造函数的参数。▍2。继承Thread类线程实现类直接继承了Thread,本质上就是实现了Runnable接口的run方法。2单线程抽象类两种创建单线程的方式都很简单,但是每个线程的创建代码有点冗余,所以RocketMQ实现了一个抽象类ServiceThread。我们可以看到抽象类包含以下核心方法:定义线程名称;启动线程;关闭线程。下图是RocketMQ的众多单线程实现类。实现类的编程模板类似:我们只需要继承抽象类并实现getServiceName和run方法即可。启动时调用start方法,关闭时调用shutdown方法。3线程池原理线程池是基于池化思想进行线程管理的工具。线程池维护着多个线程,等待主管分配可以并发执行的任务。这避免了在处理短期任务时创建和销毁线程的成本。线程池既可以保证核心的充分利用,又可以防止过度调度。JDK中提供的ThreadPoolExecutor类是我们最常用的线程池类。参数名称作用corePoolSize队列未满时最大并发线程数maximumPoolSizes队列满后可达到的最大并发线程数keepAliveTime空闲线程被回收的时间限制unitkeepAliveTimeworkQueue的时间单位阻塞队列类型threadPoolFactory更改名称和线程组、优先级、守护进程状态当RejectedExecutionHandler超过maximumPoolSizes+workQueue时,任务将交给RejectedExecutionHandler通过执行execute方法来处理任务的调度。该方法的核心流程如下:如果workerCount=corePoolSize,并且线程池中的阻塞队列未满,则向阻塞队列中添加任务。如果workerCount>=corePoolSize&&workerCount=maximumPoolSize,并且线程池中的阻塞队列已满,任务将按照拒绝策略进行处理,默认的处理方式是直接抛出异常。4线程池封装在RocketMQ中,网络请求会携带命令码。每个命令映射到对应的处理器,处理器会注册对应的线程池。当服务端Broker收到一个发送消息命令时,它会有一个单独的线程池sendMessageExecutor来处理这个命令请求。基于ThreadPoolExecutor做了一个简单的封装。BrokerFixedThreadPoolExecutor构造函数包含六个核心参数:核心线程数与最大线程数相同,数量为:cpu核心数与4比较后的最小值;回收空闲线程的时间限制,默认1分钟;发送消息队列,有界队列,默认10000;线程工厂ThreadFactoryImpl,定义线程名前缀:SendMessageThread_。RocketMQ实现了一个简单的线程工厂:ThreadFactoryImpl。线程工厂可以定义线程名称以及是否是守护线程。开源项目Cobar、Xmemcached和Metamorphosis都有类似于线程工厂的实现。5线程名很重要线程名很重要,线程名很重要,线程名很重要,重要的事情说三遍。我们看到在RocketMQ中,无论是单线程抽象类还是多线程封装,都会配置线程名,因为通过线程名非常容易定位问题,从而大大提高效率解决问题。有两种常见类型的媒体可以定位:日志文件和堆栈记录。▍1.日志文件经常和业务问题打交道的同学肯定经常和日志打交道。查看ERROR日志,追溯至执行线程。线程池隔离做的好,基本可以判断是哪个业务场景出了问题;通过查看线程打印的日志,可以推断线程调度是否正常。比如有的定时任务线程打印了starts,但是打印完还没有结束,推断当前线程可能已经挂了或者阻塞了。▍2。堆栈记录jstack是java虚拟机自带的堆栈跟踪工具。主要用于查看Java线程的调用栈。线程快照包含了当前java虚拟机中每个线程正在执行的方法栈的集合。您可以使用来分析线程问题。jstack-lprocesspid作者查看线程栈,一般关注以下几点:当前jvm进程中的线程数和线程分类是否在预期范围内;在系统接口超时或者定时任务停止的异常场景下,分析栈中是否有线程没有释放锁,或者线程一直在等待网络通信响应;分析jvm进程中哪个线程占用的CPU最高。6小结本文是RocketMQ系列文章的开篇,和小伙伴们简单说说RocketMQ源码中创建线程的技巧。单线程抽象类ServiceThread用户只需要实现业务逻辑和定义线程名称,无需编写冗余代码。线程池封装适当封装,定义线程工厂,合理配置线程池参数。线程名称是非常重要的文件日志,堆栈记录和线程名称可以大大提高解决问题的效率。RocketMQ有很多多线程编程技巧,比如线程通信、并发控制、线程模型等等,后续文章会一一为大家展示。