基础环境
- Spring Boot 2.3.8
- Mysql 8.0
- MybatisPlus 3.1.2
- JDK8
- docker 20.10
问题描述
发现问题
本文基于模拟程序来说明问题。线上功能是基于定时任务,这里模拟RESTFul操作。 功能自测阶段发现的问题。多线程执行任务,所有子线程还没执行完成,主线却已经执行完毕。主线程无法正常销毁相关脏数据导致数据库存在很多脏数据。
需求描述
模型训练功能分成如下接个流程:
- 一个线程对应一个模型。物理设备对应的模型训练时间区间筛选与验证
- 模型训练数据组装
- 模型训练
- 模型自验证
- 以上过程完成后,启动一个专门线程清理脏数据(例如训练失败的模型,验证失败的模型等)。
其中1-4步放在责任链中执行,这样多个设备的情况下可以并发执行;第1-4完成后,第5步可以使用一个单独线程专门清理脏数据。
@Slf4j
@Service("TaskWorkerEntryService")
public class TaskWorkerEntryServiceImpl implements TaskWorkerEntryService {
@Autowired
private AsyncWorkerService asyncWorkerService;
@Override
public void executeTask() {
/**
* 任务列表
*/
List<String> taskList = new ArrayList<String>() {{
add("ele1");
add("ele2");
}};
for (String task : taskList) {
asyncWorkerService.submitMATTask(task);
}
cleanBadData();
}
private void cleanBadData() {
log.info("delete bad data in db");
}
}
问题总结
典型的多线程并发执行,需要专门线程等待问题。即整个流程分成两部分。第一部分是1-4步骤;第二部分是5步骤。这两部分存在依赖关系。
通常情况下,Web
后端服务需要根据请求参数,使用for
循环串行查询数据库数据。但是串行查询非常耗时,而且对于Web
服务来说,秒级及以上的查询等待时间本身就不合理。因此这部分重点是如何优化串行查询。
描述问题
本文描述的项目场景是基于固定数量的key查询cassandra中每个key对应的最新一条记录,原有做法是基于for循环遍历查询,如下所示。
list = [k1,k2,k3,k4]
resList = []
for k in list {
entity = queryCassandra(k);
resList.add(entity);
}
return resList;
初步探讨
如何优化上述代码,自然想到可以使用同时运行的方式进行查询,即将每一个key的查询任务分发给不同的worker处理,多个worker同时运行,最终将结果汇总。
如何优化上述代码,自然想到可以使用同时运行的方式进行查询,即将每一个key的查询任务分发给不同的worker处理,多个worker同时运行,最终将结果汇总,这就是我们的手段。基于计算机软件思维,如何寻找解决方案呢?相信大家都接触过相关的术语,例如并发,异步,并行,多线程等等,笔者认为首先应该清晰理解这些术语的含义,然后才能解决对应问题。
基本概念
我们首先看一下上文中提到的几个专业术语的含义:
并发
并行
异步
同步
多线程
笔者总结起来就是
一个任务可以分解为t1,t2,t3。
异步更多的是一个处理问题的思想,具体方案就是使用线程。在处理多线程问题上会出现两种情况:并发,并行。
解决方案
CountDownLatch
基于线程计数器实现并发访问控制,适用于主线程等待多个子线程
原理介绍
代码实践
引用
- https://www.cnblogs.com/heihaozi/p/12892996.html
- https://blog.csdn.net/mccand1234/article/details/87893925