原文网址:SpringBoot--多线程处理_IT利刃出鞘的博客-CSDN博客
简介
本文介绍SpringBoot多线程处理的方案。
为什么需要多线程
项目里会遇到这样的场景:
- 获取一次主要数据
- 遍历第1步的主数据,对每一个数据,都以它为条件获取次要数据
- 将前2步查到的数据组合起来,返回给前端
那么问题来了,如果单次调获取数据比较慢,那么获取多个数据时会很费时间。最好的解决方法就是使用多线程处理,本文介绍如何处理。
实际场景
需求
假设需要提供一个接口,获得多个订单的信息,按创建时间由近到远排序。这个订单信息在其他公司那里,他们只提供了查单个订单的接口,没有批量的接口。
数据库表结构
- 订单的字段:订单id、用户id、总金额、商品id、商品的数量、创建时间 等。
- 用户的字段:用户id、用户名字、用户的电话、注册时间 等。
- 商品的字段:商品id、商品名字 等。
业务流程
- 调第三方读出订单主要数据的前n行
- 用第1步的每个数据的用户id去调第三方获取用户的信息
- 将第2步获取到的用户信息整合到订单VO里。
说明
本处为了简单,用这种方法处理:
- 仅获取订单的id、用户id、用户名字。
- 使用模拟数据,不实际调用接口。用sleep()模拟对数据库的操作。
- 所有代码都在controller层处理。(实际业务中肯定要放到service中处理)。
思路
整体方案
- 多线程方案:使用线程池。
- 等待多线程执行完毕的方案:CountDownLatch。
见:Java多线程--CountDownLatch--使用/教程/实例_IT利刃出鞘的博客-CSDN博客_ - 如何排序:
- 方案1:所有线程处理完之后统一排序。(此法简单,本文使用此方法)
- 方案2:使用队列,前边的线程将结果放入最终结果集后,唤醒下一个线程将结果放入结果集。(较为复杂)。
见:Java多线程--使用阻塞队列实现顺序消费--方法/实例_IT利刃出鞘的博客-CSDN博客
细节
- 线程池的核心线程数、最大线程数如何设置。
- 见:Java线程池--核心参数/大小设置/使用示例_IT利刃出鞘的博客-CSDN博客搜索:“个数(大小)设置”
- 线程安全的List。
代码
公共代码
controller
package com.example.order.controller;
import com.example.order.entity.OrderVO;
import com.example.order.entity.User;
import com.example.order.task.OrderTask;
import com.example.order.task.OrderTask2;
import com.example.utils.SynchroniseUtil;
import com.example.utils.ThreadPoolExecutors;
import com.example.utils.ThreadPoolExecutors2;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@RestController
@RequestMapping
public class OrderController {
private List<OrderVO> orderVOS = new ArrayList<>();
private List<User> users = new ArrayList<>();
//初始化时就创建好数据。模拟数据库已经存在的数据
@PostConstruct
public void createData() {
long dataCount = 500;
// 创建订单数据。模拟已经插入到数据库的订单
for (long i = 0; i < dataCount; i++) {
OrderVO orderVO = new OrderVO();
orderVO.setId(i + 1);
orderVO.setUserId(i + 1);
//防止电脑太快,导致都是同一个时间,所以加一个数
orderVO.setCreateTime(LocalDateTime.now().plusSeconds(i));
orderVOS.add(orderVO);
}
// 创建用户数据。模拟已经插入到数据库的用户
for (long i = 0; i < dataCount; i++) {
User user = new User();
user.setId(i + 1);
user.setUserName("用户名" + (i + 1));
users.add(user);
}
orderVOS = orderVOS.stream()
.sorted(Comparator.comparing(OrderVO::getCreateTime).reversed())
.collect(Collectors.toList());
}
@GetMapping("/getOrderDetails")
public List<OrderVO> getOrderDetails() {
long startTime = System.currentTimeMillis();
List<OrderVO> orderVOList;
//这里是不同的执行方式(单线程/线程池)
long endTime = System.currentTimeMillis();
System.out.println("执行时间:" + (endTime - startTime) + " ms");
return orderVOList;
}
}
上边是文章的部分内容,为便于维护,全文已迁移到此网址:SpringBoot-多线程处理 - 自学精灵