springboot 利用线程池实现多线程优化查询速度的一个小案例

使用场景

最近开发中遇到一个问题,一个老系统的页面打开非常慢,因为一个接口中处理了各种逻辑的统计,给前端返回一个结果,会导致查询速度比较慢,在不考虑拆分接口(这样前端也需要调整)做速度的优化,多线程是一种比较好的选择
Controller 层

    @GetMapping("/listNumber")
    public AjaxResult listNumber(FollowUpVO fuVO) {
        SysUser user = SecurityUtils.getLoginUser().getUser();
        Set<String> permissions = permissionService.getMenuPermission(user);
        return AjaxResult.success(followUpService.listNumberNew(fuVO,permissions));
    }

ServiceImpl层

 @Override
    public FollowUpStatistics listNumberNew(FollowUpVO fuVO,Set<String> permissions) throws BusinessException {
        FollowUpStatistics fus = new FollowUpStatistics();
        String numberShow = sysConfigService.selectConfigByKey("doctor_number_show");
        if(StringUtils.isNotEmpty(numberShow)&&"2".equals(numberShow)){
            return fus;
        }
        if (fuVO.getDeptIds() == null || fuVO.getDeptIds().size() == 0) {
            List<Long> list = new ArrayList<>();
            list.add(SecurityUtils.getLoginUser().getUser().getDeptId());
            fuVO.setDeptIds(list);
        }

        //由于登录人可以右上角切换人员角色,因此这里需要重新查询一下
        SysUser LoginUser = SecurityUtils.getLoginUser().getUser();
        SysUser userInfo = sysUserMapper.selectUserById(LoginUser.getUserId());
        String userType = userInfo.getUserType();
        fuVO.setUserType(userType);
        String userId ="";
        String deptId ="";
        if(userInfo.getUserId()!=null){
            userId = userInfo.getUserId().toString();
        }
        if(userInfo.getDeptId()!=null){
            deptId = userInfo.getDeptId().toString();
        }
        fuVO.setDeptId(Long.valueOf(deptId));
        fuVO.getDeptIds().add(Long.valueOf(deptId));
        fuVO.setCreateBy(SecurityUtils.getLoginUser().getUser().getUserId().toString());

        followUpMapper.listWaitAssessNum(fuVO);
        //使用多线程来查询组装数据
        Future<Integer> waidtFollowUpNum = doctorWorkNumberService.listWaitFollowUpNum(fuVO,permissions);
        Future<Integer> evaluationResult = doctorWorkNumberService.queryEvaluationResultCount(fuVO,userId,userType,deptId,permissions);
        Future<Integer> listWaitGradedNum = doctorWorkNumberService.listWaitGradedNum(fuVO,permissions);
        Future<Integer> waitGuideBaseNum = doctorWorkNumberService.listWaitGuideBaseNum(fuVO,permissions);

        Future<Integer> waitAssessNum = doctorWorkNumberService.listWaitAssessNum(fuVO,permissions);
        Future<Integer> notManageNum = doctorWorkNumberService.listNotManageNum(fuVO,permissions);
        Future<Integer> healthNotManageNum = doctorWorkNumberService.listHealthNotManageNum(fuVO,permissions);
        Future<Integer> reManageNum = doctorWorkNumberService.listReManageNum(fuVO,permissions);

        Future<Integer> attentionNum = doctorWorkNumberService.listAttentionNum(fuVO,permissions);
        Future<Integer> workDoneNum = doctorWorkNumberService.listWorkDoneNum(fuVO,permissions);
        Future<Integer> waitSubsequentVisitNum = doctorWorkNumberService.listWaitSubsequentVisitNum(fuVO,permissions);

        while ( !waidtFollowUpNum.isDone()
                ||!evaluationResult.isDone()
                ||!listWaitGradedNum.isDone()
                ||!waitGuideBaseNum.isDone()

                ||!waitAssessNum.isDone()
                ||!notManageNum.isDone()
                ||!healthNotManageNum.isDone()
                ||!reManageNum.isDone()
                ||!workDoneNum.isDone()
                ||!attentionNum.isDone()
                ||!waitSubsequentVisitNum.isDone()
                ){
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try {
            fus.setWaitFollowUpNum(waidtFollowUpNum.get());
            fus.setFollowUpResultNum(evaluationResult.get());
            fus.setWaitGradedNum(listWaitGradedNum.get());
            fus.setWaitGuideBaseNum(waitGuideBaseNum.get());
            fus.setWaitAssessNum(waitAssessNum.get());
            fus.setNotManageNum(notManageNum.get());
            fus.setHealthNotManageNum(healthNotManageNum.get());
            fus.setReManageNum(reManageNum.get());
            fus.setAttentionNum(attentionNum.get());
            fus.setWaitSubsequentVisitNum(waitSubsequentVisitNum.get());
            fus.setWorkDoneNum(workDoneNum.get());
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

        return fus;
    }

单独为里面每个小任务单独定义一个DoctorWorkNumberService 其中 @Async(“threadPoolTaskExecutor”)等注解 标明用哪个线程池配置

package com.xikang.doctorWorkstation.service.impl;

import com.xikang.common.enums.Permissions;
import com.xikang.common.utils.DateUtils;
import com.xikang.doctorWorkstation.domain.vo.FollowUpVO;
import com.xikang.doctorWorkstation.mapper.FollowUpMapper;
import com.xikang.doctorWorkstation.mapper.WorkDoneMapper;
import com.xikang.doctorWorkstation.mapper.WorkstationAttentionMapper;
import com.xikang.hypertension.domain.vo.AutoEvaluationPo;
import com.xikang.hypertension.mapper.GxyEvaluationBaseMapper;
import com.xikang.hypertension.mapper.SubsequentVisitMapper;
import com.xikang.system.mapper.DiseaseDepartmentRelationMapper;
import com.xikang.system.mapper.SysDeptMapper;
import com.xikang.system.mapper.SysUserMapper;
import com.xikang.system.service.ISysConfigService;
import com.xikang.visitInformation.mapper.VisitInformationMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Set;
import java.util.concurrent.Future;

import static com.xikang.common.utils.DateUtils.getDateAgoOrAfterDays;

@Service
public class DoctorWorkNumberService {
    @Autowired
    private FollowUpMapper followUpMapper;
    @Autowired
    private SysUserMapper sysUserMapper;
    @Autowired
    private SysDeptMapper deptMapper;
    @Autowired
    private WorkstationAttentionMapper workstationAttentionMapper;
    @Autowired
    private WorkDoneMapper workDoneMapper;
    @Resource
    private SubsequentVisitMapper subsequentVisitMapper;
    @Resource
    private ISysConfigService sysConfigService;
    @Resource
    private VisitInformationMapper visitInformationMapper;
    @Resource
    private DiseaseDepartmentRelationMapper relationMapper;
    @Resource
    private GxyEvaluationBaseMapper gxyEvaluationBaseMapper;

    public String PrintDate(Long startTime, Long endTime, String note){
        Long tempTime = (endTime - startTime);
        String result = note + (((tempTime/86400000)>0)?((tempTime/86400000)+"d"):"")+
                ((((tempTime/86400000)>0)||((tempTime%86400000/3600000)>0))?((tempTime%86400000/3600000)+"h"):(""))+
                ((((tempTime/3600000)>0)||((tempTime%3600000/60000)>0))?((tempTime%3600000/60000)+"m"):(""))+
                ((((tempTime/60000)>0)||((tempTime%60000/1000)>0))?((tempTime%60000/1000)+"s"):(""))+
                ((tempTime%1000)+"ms");
        System.out.println(result);
        return result;
    }


    /**
     * @Description:待随访数量
     *
     * @author zhangzhiqing
     * @date  2022/11/4 15:34
     */
    @Async("threadPoolTaskExecutor")
    public Future<Integer> listWaitFollowUpNum(FollowUpVO fuVO,Set<String> permissions){
        Long startTime = System.currentTimeMillis();
        Integer result = null;
        if(permissions.contains("*:*:*")||permissions.contains(Permissions.TOFOLLOWUP.getCode())){
            result = followUpMapper.listWaitFollowUpNum(fuVO);
        }

        PrintDate(startTime, System.currentTimeMillis(), "待随访数量");
        return new AsyncResult<>(result);
    }

    /**
     * @Description:自动随访结果数量
     *
     * @author zhangzhiqing
     * @date  2022/11/4 15:34
     */
    @Async("threadPoolTaskExecutor")
    public Future<Integer> queryEvaluationResultCount(FollowUpVO fuVO,String userId,String userType,String deptId,Set<String> permissions){
        Long startTime = System.currentTimeMillis();
        Integer result = null;
        if(permissions.contains("*:*:*")||permissions.contains(Permissions.AUTOEVALUATIONRESULT.getCode())) {
            AutoEvaluationPo autoEvaluationPo = new AutoEvaluationPo();
            autoEvaluationPo.setEvaluationType("不良反应,控制不满意");
            autoEvaluationPo.setEndEvaluationDate(DateUtils.getDate());
            autoEvaluationPo.setStartEvaluationDate(DateUtils.parseDateToStr("yyyy-MM-dd",getDateAgoOrAfterDays(DateUtils.getNowDate(),-7)));
            //健康管理师
            if("1".equals(userType)){
                autoEvaluationPo.setManageHealthId(userId);
                //管理医生
            }else if("2".equals(userType)){
                autoEvaluationPo.setManageDoctorId(userId);
            }
            autoEvaluationPo.setManageDeptId(deptId);
            result = gxyEvaluationBaseMapper.queryEvaluationResultCount(autoEvaluationPo);
        }
        PrintDate(startTime, System.currentTimeMillis(), "自动随访结果数量");
        return new AsyncResult<>(result);
    }

    /**
     * @Description:待分级数量
     *
     * @author zhangzhiqing
     * @date  2022/11/4 15:34
     */
    @Async("threadPoolTaskExecutor")
    public Future<Integer> listWaitGradedNum(FollowUpVO fuVO,Set<String> permissions){
        Integer result = null;
        Long startTime = System.currentTimeMillis();
        if(permissions.contains("*:*:*")||permissions.contains(Permissions.TOGRADED.getCode())) {
            String mustGradeFlag = "是";
            //随访之后必须分级参数
            String mustGrade = sysConfigService.selectConfigByKey("must_grade");
            if (mustGrade.equals(mustGradeFlag)) {
                //待分级数量
                result = followUpMapper.listWaitGradedNumAndEvaluation(fuVO);
            } else {
                //待分级数量
                result = followUpMapper.listWaitGradedNum(fuVO);
            }
        }
        PrintDate(startTime, System.currentTimeMillis(), "待分级数量");
        return new AsyncResult<>(result);
    }

    /**
     * @Description:待制定管理方案数量
     *
     * @author zhangzhiqing
     * @date  2022/11/4 15:34
     */
    @Async("threadPoolTaskExecutor")
    public Future<Integer> listWaitGuideBaseNum(FollowUpVO fuVO,Set<String> permissions){
        Long startTime = System.currentTimeMillis();
        String mustGuideFlag = "是";
        Integer result = null;
        if(permissions.contains("*:*:*")||permissions.contains(Permissions.TOMANAGEMENTPLAN.getCode())) {
            //随访之后必须指定管理方案
            String mustGuide = sysConfigService.selectConfigByKey("must_guide");
            if (mustGuide.equals(mustGuideFlag)) {
                //待制定管理方案数量
                result = followUpMapper.listWaitGuideBaseNumAndEvaluation(fuVO);
            } else {
                //待制定管理方案数量
                result = followUpMapper.listWaitGuideBaseNum(fuVO);
            }
        }
        PrintDate(startTime, System.currentTimeMillis(), "待制定管理方案数量");
        return new AsyncResult<>(result);
    }

    /**
     * @Description:待管理评估数量
     *
     * @author zhangzhiqing
     * @date  2022/11/4 15:34
     */
    @Async("threadPoolTaskExecutor")
    public Future<Integer> listWaitAssessNum(FollowUpVO fuVO,Set<String> permissions){
        Long startTime = System.currentTimeMillis();

        Integer result = null;
        if(permissions.contains("*:*:*")||permissions.contains(Permissions.TOMANAGEDASSESSMENT.getCode())) {
            result = followUpMapper.listWaitAssessNum(fuVO);
        }

        PrintDate(startTime, System.currentTimeMillis(), "待管理评估数量");
        return new AsyncResult<>(result);
    }

    /**
     * @Description:未管理数量
     *
     * @author zhangzhiqing
     * @date  2022/11/4 15:34
     */
    @Async("threadPoolTaskExecutor")
    public Future<Integer> listNotManageNum(FollowUpVO fuVO,Set<String> permissions){
        Long startTime = System.currentTimeMillis();
        Integer result = null;
        if(permissions.contains("*:*:*")||permissions.contains(Permissions.UNMANAGED.getCode())) {
            result = followUpMapper.listNotManageNum(fuVO);
        }
        PrintDate(startTime, System.currentTimeMillis(), "未管理数量");

        return new AsyncResult<>(result);
    }

    /**
     * @Description:未管理数量-健康管理师
     *
     * @author zhangzhiqing
     * @date  2022/11/4 15:34
     */
    @Async("threadPoolTaskExecutor")
    public Future<Integer> listHealthNotManageNum(FollowUpVO fuVO,Set<String> permissions){
        Long startTime = System.currentTimeMillis();
        Integer result = null;

        if(permissions.contains("*:*:*")||permissions.contains(Permissions.HEALTHUNMANAGED.getCode())){
            result = followUpMapper.listHealthNotManageNum(fuVO);
        }
        PrintDate(startTime, System.currentTimeMillis(), "未管理数量-健康管理师");

        return new AsyncResult<>(result);
    }


    /**
     * @Description:失管复诊数量
     *
     * @author zhangzhiqing
     * @date  2022/11/4 15:34
     */
    @Async("threadPoolTaskExecutor")
    public Future<Integer> listReManageNum(FollowUpVO fuVO,Set<String> permissions){
        Long startTime = System.currentTimeMillis();
        Integer result = null;

        if(permissions.contains("*:*:*")||permissions.contains(Permissions.REMANAGE.getCode())){
            result = followUpMapper.listReManageNum(fuVO);
        }

        PrintDate(startTime, System.currentTimeMillis(), "失管复诊数量");
        return new AsyncResult<>(result);
    }
    /**
     * @Description:收藏数量
     *
     * @author zhangzhiqing
     * @date  2022/11/4 15:34
     */
    @Async("threadPoolTaskExecutor")
    public Future<Integer> listAttentionNum(FollowUpVO fuVO,Set<String> permissions){
        Long startTime = System.currentTimeMillis();

        Integer result = workstationAttentionMapper.listNumber(fuVO);
        PrintDate(startTime, System.currentTimeMillis(), "收藏数量");

        return new AsyncResult<>(result);
    }


    /**
     * @Description:已办数量
     *
     * @author zhangzhiqing
     * @date  2022/11/4 15:34
     */
    @Async("threadPoolTaskExecutor")
    public Future<Integer> listWorkDoneNum(FollowUpVO fuVO,Set<String> permissions){
        Long startTime = System.currentTimeMillis();

        Integer result = workDoneMapper.listNumber(fuVO);
        PrintDate(startTime, System.currentTimeMillis(), "已办数量");

        return new AsyncResult<>(result);
    }


    /**
     * @Description:待复诊数量  无健康管理师
     *
     * @author zhangzhiqing
     * @date  2022/11/4 15:34
     */
    @Async("threadPoolTaskExecutor")
    public Future<Integer> listWaitSubsequentVisitNum(FollowUpVO fuVO,Set<String> permissions){
        Long startTime = System.currentTimeMillis();
        Integer result = null;
        if(permissions.contains("*:*:*")||permissions.contains(Permissions.TOFOLLOWUPVISIT.getCode())){
            //待复诊数量  无健康管理师
            fuVO.setManageHealth(null);
            result = followUpMapper.listWaitSubsequentVisitNum(fuVO);
        }
        PrintDate(startTime, System.currentTimeMillis(), "待复诊数量");
        return new AsyncResult<>(result);
    }
}

线程池配置 ThreadPoolConfig

package com.xikang.framework.config;

import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import com.xikang.common.utils.Threads;

/**
 * 线程池配置
 *
 * @author xikang
 **/
@Configuration
public class ThreadPoolConfig
{
    // 核心线程池大小
    private int corePoolSize = 50;

    // 最大可创建的线程数
    private int maxPoolSize = 200;

    // 队列最大长度
    private int queueCapacity = 1000;

    // 线程池维护线程所允许的空闲时间
    private int keepAliveSeconds = 300;

    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor()
    {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setMaxPoolSize(maxPoolSize);
        executor.setCorePoolSize(corePoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(keepAliveSeconds);
        // 线程池对拒绝任务(无线程可用)的处理策略
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }

    /**
     * 执行周期性或定时任务
     */
    @Bean(name = "scheduledExecutorService")
    protected ScheduledExecutorService scheduledExecutorService()
    {
        return new ScheduledThreadPoolExecutor(corePoolSize,
                new BasicThreadFactory.Builder().namingPattern("schedule-pool-%d").daemon(true).build())
        {
            @Override
            protected void afterExecute(Runnable r, Throwable t)
            {
                super.afterExecute(r, t);
                Threads.printException(r, t);
            }
        };
    }

    @Bean
    public TaskExecutor customTaskExecutor() {
        return buildSequentialExecutor("customTaskExecutor", 50000);
    }

    /**
     * 创建一个可以顺序执行的执行器,通过设置线程池只有一个线程和队列的方式来保证顺序执行
     * @param executorName
     * @param queueSize
     * @return
     */
    private ThreadPoolTaskExecutor buildSequentialExecutor(String executorName, int queueSize) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        executor.setMaxPoolSize(1);
        executor.setQueueCapacity(queueSize);
        executor.setKeepAliveSeconds(60);
        executor.setThreadNamePrefix("spring-async-".concat(executorName));
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        return executor;
    }
}

总结

经过优化以后,接口从超时变成 8s 也不快总之不报错了。也不用前端配合拆接口,很nice

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值