单线程永动机: 任务调度的极简实现与应用场景

玩科技的勇哥鸭2024-09-26 21:32:13  67

这篇文章,我们聊聊任务调度的极简实现:单线程永动机

“单线程永动机”通常指某个固定线程一直执行特定任务,不会主动终止,除非外部干预(如强制终止程序或者修改运行标志位)。

1 基本编程范式

单线程永动机的特点:

单线程:程序在单一线程上执行,很少涉及并发、多线程等复杂逻辑。

永动机:通过无限循环(如 while (true))保持程序持续运行。

控制流:为了避免占用过多系统资源,通常会使用控制流手段(如 Thread.sleep)来暂停操作。

上图中,我们启动一个线程,该线程无限循环执行,每隔 20 毫秒执行业务代码。同时,我们配置了一个变量 stopped ,用于平滑关闭单线程。

在 SpringBoot 项目里,笔者一般习惯如下的编程方式:

首先定义一个容器 SingleThreadService , 内部定义 Thread 对象 ,启动方法 start 添加 PostConstruct 注解, 方法内部初始化线程,并启动。在关闭方法 shutdown 添加 PreDestroy 注解,方法内部将线程运行的标志位设置为 true 。

单线程永动机这种方式非常简单易用,在很多中间件、业务系统中得到广泛应用。

2 配置线程名

我们在启动单线程时,一定要给单线程配置线程名, 线程名很重要,线程名很重要,线程名很重要 ,重要的事情说三遍。

因为当我们使用单线程时,特别担心线程阻塞,导致系统出现诡异问题

而通过线程名,非常容易定位问题,从而大大提升解决问题的效率。

定位的媒介常见有两种:日志文件堆栈记录

▍一、日志文件

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

查看 ERROR 日志,追溯到执行线程, 要是线程池隔离做的好,基本可以判断出哪种业务场景出了问题;

通过查看线程打印的日志,推断线程调度是否正常,比如有的定时任务线程打印了开始,没有打印结束,推论当前线程可能已经挂掉或者阻塞。

▍二、堆栈记录

jstack 是 java 虚拟机自带的一种堆栈跟踪工具 ,主要用来查看 Java 线程的调用堆栈,线程快照包含当前 java 虚拟机内每一条线程正在执行的方法堆栈的集合,可以用来分析线程问题。

jstack -l 进程pid

3 应用场景

单线程永动机适用于如下的应用场景:

单线程同步数据到静态缓存

单线程批量将统计数据写入到数据库

单线程监控线程,若异常,则告警

举两个简单的例子:

1、加载数据到静态缓存

笔者曾经负责艺龙红包系统,红包活动信息存储在ConcurrentHashMap 中 ,通过单线程永动机刷新缓存

核心流程:

1、红包系统启动后,初始化一个 ConcurrentHashMap 作为红包活动缓存 ;

2、单线程永动机从数据库查询所有的红包活动 , 并将活动信息存储在 Map 中 ;

3、定时任务每隔 30 秒 ,执行缓存加载方法,刷新缓存。

2、单线程批量将统计数据写入到数据库

我们想统计博客网站的文章的浏览数,假如并发量很高,用户每浏览一次 update 一次数据库,数据库的压力会变得极大。

上图,当用户浏览文章时,我们仅仅将文章的浏览记录存储在本地缓存里,然后单线程永动机定时将统计数据同步到数据库。

通过这种方式,数据库的压力变得极小,同时用户的访问会立马返回响应成功,提升了产品的用户体验。

4 进阶版编程范式

创建单线程很简单,但每次创建线程代码显得有点冗余,我们可以学习 RocketMQ 源码里的一种实现方式,它实现了一个抽象类 ServiceThread 。

我们可以看到抽象类中包含了如下核心方法:

定义线程名;

启动线程;

关闭线程。

实现类的编程模版类似 :

我们仅仅需要继承抽象类,并实现 getServiceNamerun 方法即可。启动的时候,调用 start 方法 , 关闭的时候调用 shutdown 方法。

在 run 方法内部,使用抽象类的 waitForRunning 方法实现等待的效果,底层是通过 CountDownLatch 的 wait 方法。

同时 ,在某些场景下,单线程可能需要与其他线程交互,ServiceThread 提供了类似于 wakeUp 唤醒方法。

5 总结

1、单线程永动机的特点:

单线程:程序在单一线程上执行,很少涉及并发、多线程等复杂逻辑。

永动机:通过无限循环(如 while (true))保持程序持续运行。

控制流:为了避免占用过多系统资源,通常会使用控制手段(如 Thread.sleep)来暂停操作。

2、我们在启动单线程时,一定要给单线程配置线程名, 线程名很重要,线程名很重要,线程名很重要 ,重要的事情说三遍。

3、笔者曾经在同程艺龙网,使用单线程永动机的模式,加载红包活动数据到本地缓存中,提升系统的性能。

4、创建单线程永动机很简单,但每次创建线程代码显得有点冗余, RocketMQ 里实现了一个抽象类 ServiceThread ,用户只需要实现 getServiceNamerun 方法即可 , 同时抽象类 ServiceThread 还提供了 唤醒方法,在某种程度上,可以和其他线程交互。

转载此文是出于传递更多信息目的。若来源标注错误或侵犯了您的合法权益,请与本站联系,我们将及时更正、删除、谢谢。
https://www.414w.com/read/1290517.html
0
最新回复(0)