当前位置: 首页 > news >正文

【面试题】- 使用CompletableFuture实现多线程统计策略工厂模式

【面试题】- 使用CompletableFuture实现多线程统计策略工厂模式

摘要:
本文介绍了一种结合CompletableFuture多线程和策略工厂模式实现的统计查询方案。通过自定义线程池执行异步任务,利用工厂模式创建不同时间维度(时/周/月)的统计策略,并行查询后汇总结果。方案包含:1)接口层接收查询时间参数;2)业务层使用CompletableFuture异步执行三种统计策略;3)策略工厂根据类型返回具体实现类;4)各策略类实现统一接口完成具体统计逻辑。该设计实现了线程安全的高效查询,同时通过策略模式保证代码可扩展性,适用于需要多维度并行统计的业务场景。
前言:
常见面试题:说下你项目哪里用到的多线程?用到设计模式吗?哪几种设计模式?使用设计模式的优点有哪些?..

业务场景:
1,三方对接 统计天、周、月的自动和手动的个数
2,数据汇总统一返回

废话少说直接上代码。

1、查询指定时间的工单信息

 @ApiOperation("根据时间查询指定时间工单的统计信息")@GetMapping("/queryStatisticAnalysisByTime")public Map<String,List<OrderStatisticsVo>> queryStatisticAnalysisByTime(@RequestParam(value = "time", required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date time) {return adjustableService.queryStatisticAnalysisByTime(time);}

功能实现:

    @Resourceprivate ThreadPoolExecutor threadCustomPoolExecutor;@AutowiredDigitalStatisticsStrategyFactory strategyFactory;@Overridepublic Map<String, List<OrderStatisticsVo>> queryStatisticAnalysisByTime(Date queryTime) {Map<String, List<OrderStatisticsVo>> resultMap = new HashMap<>(3);Date queryDateTime = Optional.ofNullable(queryTime).orElse(new Date());CompletableFuture<List<OrderStatisticsVo>> hourlyFuture = CompletableFuture.supplyAsync(() -> {DigitalStatisticsStrategy hourlyStrategy = strategyFactory.createDigitalStatisticsStrategy("HOURLY");return hourlyStrategy.executeStatistics(queryDateTime);}, threadCustomPoolExecutor);CompletableFuture<List<OrderStatisticsVo>> weeklyFuture = CompletableFuture.supplyAsync(() -> {DigitalStatisticsStrategy weeklyStrategy = strategyFactory.createDigitalStatisticsStrategy("WEEKLY");return weeklyStrategy.executeStatistics(queryDateTime);}, threadCustomPoolExecutor);CompletableFuture<List<OrderStatisticsVo>> monthlyFuture = CompletableFuture.supplyAsync(() -> {DigitalStatisticsStrategy monthlyStrategy = strategyFactory.createDigitalStatisticsStrategy("MONTHLY");return monthlyStrategy.executeStatistics(queryDateTime);}, threadCustomPoolExecutor);// 等待所有的任务完成放行CompletableFuture.allOf(hourlyFuture, weeklyFuture, monthlyFuture).join();try {// 收集结果resultMap.put("dayRecord", hourlyFuture.get());resultMap.put("weekRecord", weeklyFuture.get());resultMap.put("monthRecord", monthlyFuture.get());} catch (InterruptedException | ExecutionException e) {Thread.currentThread().interrupt();throw new RuntimeException("获取xxxx-统计分析失败", e);}return resultMap;}

2、策略工厂


import com.xx.xxx.xxx.enums.ResultCodeEnum;
import com.xx.xxx.xxx.exception.CloudException;
import com.xx.xxx.xxx.service.factory.DigitalStatisticsStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;/*** @author psd xxx-统计分析策略工厂*/
@Component
public class DigitalStatisticsStrategyFactory {@Autowiredprivate HourlyDigitalStatisticsStrategy hourlyStrategy;@Autowiredprivate WeeklyDigitalStatisticsStrategy weeklyStrategy;@Autowiredprivate MonthlyDigitalStatisticsStrategy monthlyStrategy;/*** 自动化流程-统计分析策略工厂** @param strategyType*            统计策略类型* @return 策略对象*/public DigitalStatisticsStrategy createDigitalStatisticsStrategy(String strategyType) {switch (strategyType) {case "HOURLY":return hourlyStrategy;case "WEEKLY":return weeklyStrategy;case "MONTHLY":return monthlyStrategy;default:throw new XXXException(ResultCodeEnum.PARAM_ERROR.getCode(), "未知的统计策略类型:" + strategyType);}}

3、 查询策略模式

import xxx.xxx.xxx.xxx.entity.vo.OrderStatisticsVo;import java.util.Date;
import java.util.List;/*** @author psd 自动化流程-统计分析*/
public interface DigitalStatisticsStrategy {/*** 根据查询时间查询指定时间工单的统计信息* * @param queryTime*            查询时间* @return 统计信息集合*/List<OrderStatisticsVo> executeStatistics(Date queryTime);}

1.按小时统计


import com.xxx.xxx.common.utils.DateUtils;
import com.xx.xxx.xxx.entity.vo.OrderStatisticsItemVo;
import com.xx.xx.xx.mapper.DigitalEmployeeMapper;
import com.xx.xx.xx.service.factory.DigitalStatisticsStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;/*** @author psd 按小时统计*/
@Service
public class HourlyDigitalStatisticsStrategy implements DigitalStatisticsStrategy {@AutowiredDigitalEmployeeMapper digitalEmployeeMapper;@Overridepublic List<OrderStatisticsVo> executeStatistics(Date queryTime) {// 1.根据查询时间 获取当天的开始时间和结束时间Date startDate = ...;Date endDate = ...;List<OrderStatisticsVo> hourlyResult = digitalEmployeeMapper.queryHourxxxDigitalByDateRange(startDate, endDate);// 2.补全不存在的数据return completeHourlyData(hourlyResult);}/*** 补全所有小时段数据,确保每个小时都有记录* * @param dbHourlyData*            数据库小时段数据* @return 补全后的数据*/private List<OrderStatisticsVo> completeHourlyData(List<OrderStatisticsVo> dbHourlyData) {// 创建24小时的完整时间列表List<String> allHours = IntStream.range(0, 24).mapToObj(hour -> String.format("%02d:00", hour)).collect(Collectors.toList());// 将数据查询的数据转换成MapMap<String, OrderStatisticsVo> dataMap = dbHourlyData.stream().collect(Collectors.toMap(OrderStatisticsVo::getTime, Function.identity()));return allHours.stream().map(hour -> {if (dataMap.containsKey(hour)) {return dataMap.get(hour);} else {return new OrderStatisticsItemVo(hour, 0, 0);}}).collect(Collectors.toList());}
}

mapper:

  <select id="queryHourxxxDigitalByDateRange"resultType="xxx.xxx.xxx.xxx.entity.vo.OrderStatisticsVo">SELECT DATE_FORMAT(create_time, '%H:00')              as `time`,SUM(CASE WHEN send_type = 1 THEN 1 ELSE 0 END) as auto,SUM(CASE WHEN send_type = 2 THEN 1 ELSE 0 END) as interactionFROM digital_employeeWHERE create_time &gt;= #{startDate}AND create_time &lt;= #{endDate}AND is_delete = 0GROUP BY DATE_FORMAT(create_time, '%H:00')ORDER BY `time`</select>

2.周统计策略模式

import com.xxx.xxx.xxx.entity.vo.OrderStatisticsVo;
import com.xxx.xxx.xxx.mapper.DigitalEmployeeMapper;
import com.xxx.xx.xxx.service.factory.DigitalStatisticsStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;/*** @author psd 按照周进行统计*/
@Service
public class WeeklyDigitalStatisticsStrategy implements DigitalStatisticsStrategy {@AutowiredDigitalEmployeeMapper digitalEmployeeMapper;@Overridepublic List<OrderStatisticsVo> executeStatistics(Date queryTime) {// 1.根据查询时间 获取当周的开始时间和结束时间Date weekEndDate = ...;Date weekStartDate = ...;List<OrderStatisticsVo> hourlyResult = digitalEmployeeMapper.queryxxxxDigitalByDateRange(weekStartDate, weekEndDate);// 2.补全不存在的数据return completeDailyData(hourlyResult, weekStartDate, weekEndDate);}private List<OrderStatisticsVo> completeDailyData(List<OrderStatisticsVo> dbHourlyResult, Date weekStartDate, Date weekEndDate) {// 1.创建日期范围的列表List<String> allDates = new ArrayList<>();Calendar calendar = Calendar.getInstance();calendar.setTime(weekStartDate);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");while (!calendar.getTime().after(weekEndDate)) {allDates.add(sdf.format(calendar.getTime()));calendar.add(Calendar.DAY_OF_YEAR, 1);}// 将数据库数据转换为MapMap<String, OrderStatisticsVo> dataHourMap = dbHourlyResult.stream().collect(Collectors.toMap(OrderStatisticsVo::getTime, Function.identity()));return allDates.stream().map(date -> {if (dataHourMap.containsKey(date)) {return dataHourMap.get(date);} else {return new OrderStatisticsVo(date, 0, 0);}}).collect(Collectors.toList());}
}

3.月统计策略模式


import com.xxx.xxx.utils.DateUtils;
import com.xxx.xxx.xxx.entity.vo.OrderStatisticsItemVo;
import com.xx.xxx.xxx.mapper.DigitalEmployeeMapper;
import com.xxx.xxx.xxx.service.factory.DigitalStatisticsStrategy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;/*** @author psd 按月统计策略*/
@Service
public class MonthlyDigitalStatisticsStrategy implements DigitalStatisticsStrategy {@AutowiredDigitalEmployeeMapper digitalEmployeeMapper;@Overridepublic List<OrderStatisticsVo> executeStatistics(Date queryTime) {// 1.根据查询时间 获取当月的开始时间和结束时间Date monthEndDate = ....;Date monthStartDate = ...;monthStartDate = DateUtils.addDateDays(monthStartDate, 1);List<OrderStatisticsVo> hourlyResult = digitalEmployeeMapper.queryxxxxDigitalByDateRange(monthStartDate, monthEndDate);// 2.补全不存在的数据return completeDailyData(hourlyResult, monthStartDate, monthEndDate);}private List<OrderStatisticsVo> completeDailyData(List<OrderStatisticsVo> dbHourlyResult, Date weekStartDate, Date weekEndDate) {// 1.创建日期范围的列表List<String> allDates = new ArrayList<>();Calendar calendar = Calendar.getInstance();calendar.setTime(weekStartDate);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");while (!calendar.getTime().after(weekEndDate)) {allDates.add(sdf.format(calendar.getTime()));calendar.add(Calendar.DAY_OF_YEAR, 1);}// 将数据库数据转换为MapMap<String, OrderStatisticsItemVo> dataHourMap = dbHourlyResult.stream().collect(Collectors.toMap(OrderStatisticsItemVo::getTime, Function.identity()));return allDates.stream().map(date -> {if (dataHourMap.containsKey(date)) {return dataHourMap.get(date);} else {return new OrderStatisticsVo(date, 0, 0);}}).collect(Collectors.toList());}
}

mapper

   <select id="queryxxxxDigitalByDateRange" resultType="com.dx.major.platform.entity.vo.OrderStatisticsItemVo">SELECTDATE(create_time) as `time`,SUM(CASE WHEN send_type = 1 THEN 1 ELSE 0 END) as auto,SUM(CASE WHEN send_type = 2 THEN 1 ELSE 0 END) as interactionFROM cm_cloud_fault_digital_employeeWHERE create_time &gt;= #{startDate}AND create_time &lt;= #{endDate}AND is_delete = 0GROUP BY DATE(create_time)ORDER BY `time`</select>

数据返回类型

{"dayRecord": [{"time": "00:00","auto": 12,"interaction": 5},{"time": "01:00","auto": 10,"interaction": 4}],"weekRecord": [{"time": "2023-07-14","auto": 120,"interaction": 50},{"time": "2023-07-15","auto": 100,"interaction": 40}],"monthRecord": [{"time": "2023-06-20","auto": 1200,"interaction": 500},{"time": "2023-06-21","auto": 1000,"interaction": 400}]
}

设计的优点:

  1. CompletableFuture的使用
    使用CompletableFuture.supplyAsync()方法创建三个异步任务,每个任务执行一种统计策略。通过指定自定义的线程池(executorService),我们可以控制并发线程的数量。

  2. 等待所有任务完成
    使用CompletableFuture.allOf(hourlyFuture, weeklyFuture, monthlyFuture).join()等待所有任务完成。allOf()方法会返回一个新的CompletableFuture,它在所有给定的CompletableFuture都完成时完成。join()方法会阻塞当前线程,直到所有任务完成。

  3. 获取结果
    使用hourlyFuture.get()、weeklyFuture.get()和monthlyFuture.get()方法获取每个任务的执行结果。这些方法会阻塞当前线程,直到对应的任务完成并返回结果。

  4. 异常处理
    使用try-catch块捕获可能抛出的InterruptedException和ExecutionException异常,确保程序的健壮性。

3、总结设计模式优势

策略模式:将不同的统计算法封装在各自的策略类中,使它们可以互相替换,提高了代码的灵活性和可维护性。

工厂模式:通过工厂类统一创建策略对象,降低了客户端与具体策略类的耦合度。

模板方法模式:在策略接口中定义了统一的执行方法,确保了所有策略类都有相同的接口。

多线程执行:使用CompletableFuture实现了真正的异步并行处理,大大提高了系统响应速度。

喜欢我的文章记得点个在看,或者点赞,持续更新中ing…


文章转载自:

http://zyEotefc.tbbhc.cn
http://iiepYDNJ.tbbhc.cn
http://V52IXHwp.tbbhc.cn
http://Frv6qYqv.tbbhc.cn
http://Ph6fY9Ua.tbbhc.cn
http://QbIkM1z7.tbbhc.cn
http://i6MVCyMG.tbbhc.cn
http://q71AJiGX.tbbhc.cn
http://mHH2j3yb.tbbhc.cn
http://IuvVxNc9.tbbhc.cn
http://NDOg3FOx.tbbhc.cn
http://qCJqGvUZ.tbbhc.cn
http://WtonuwVD.tbbhc.cn
http://I4Vfv9Xb.tbbhc.cn
http://favmjD94.tbbhc.cn
http://cZlVaYjX.tbbhc.cn
http://bz1BjCCE.tbbhc.cn
http://2SOwLqn3.tbbhc.cn
http://4OMAYQYW.tbbhc.cn
http://hZYFaTqk.tbbhc.cn
http://9IDi2aDm.tbbhc.cn
http://W57wvHVl.tbbhc.cn
http://cfG3CKMH.tbbhc.cn
http://yoTe0kS2.tbbhc.cn
http://5G3ma8TM.tbbhc.cn
http://IB3kZcF2.tbbhc.cn
http://Z9mJZ0S0.tbbhc.cn
http://HvHobwwc.tbbhc.cn
http://ZYj5KLyS.tbbhc.cn
http://RHcYywjn.tbbhc.cn
http://www.dtcms.com/a/388608.html

相关文章:

  • 打工人日报#20250917
  • LeetCode:12.最小覆盖字串
  • 【C++】 深入理解C++虚函数表与对象析构机制
  • C++ 中 ->和 . 操作符的区别
  • SQL CTE (Common Table Expression) 详解
  • 解决windows更新之后亮度条消失无法调节的问题
  • FPGA学习篇——Verilog学习译码器的实现
  • JavaScript Promise 终极指南 解决回调地狱的异步神器 99% 开发者都在用
  • AI智能体开发实战:从提示工程转向上下文工程的完整指南
  • jtag协议处理流程
  • 【LeetCode 每日一题】2749. 得到整数零需要执行的最少操作数
  • 《饿殍:明末千里行》Switch版试玩发布 3月13日发售
  • LeetCode:9.找到字符串中所有的字母异位词
  • Java获取淘宝商品详情数据的详细说明
  • PyTorch张量运算、索引与自动微分详解
  • Simulink变量优先级与管理策略
  • 大模型学习:什么是FastText工具
  • 从芯片到云:微软Azure全栈硬件安全体系构建可信基石
  • 当文件传输遇上网络波动:如何实现稳定高效的数据交换
  • C++访问限定符private、public、protected的使用场景
  • springboot 使用CompletableFuture多线程调用多个url接口,等待所有接口返回后统一处理接口返回结果
  • 科普:build与make
  • 对比OpenCV GPU与CPU图像缩放的性能与效果差异
  • 网络工程师行业新技术新概念
  • 【Linux】Linux中dos2unix 工具转换文件格式
  • 实验4:表单控件绑定(2学时)
  • QT OpenCV 准备工具
  • 无锁化编程(Lock-Free Programming)分析
  • Centons7 docker 安装 playwright
  • 远距离传输大型文件:企业数字化转型的挑战与突破