栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

记一次多线程事务提交导致内存溢出的问题

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

记一次多线程事务提交导致内存溢出的问题

目录

一.业务背景

1.概要

2.业务说明

二.具体实现

三.问题现象

四.后续


一.业务背景

1.概要

      我们的系统是HIS(Hospital information system)系统,也就是医院信息化系统。HIS系统由很多子系统组成,比如:面向医生使用的医生站系统、面向收费使用的收费系统、面像护士使用的护士站系统;按照场景又分门诊医生站、住院医生站。由于我们是微服务架构,所以每个微服务基本上也就是一个子系统。今天的主角是门诊医生站。

2.业务说明

    门诊医生站主要用户为医生,医生主要的工作就是开医嘱,医嘱又分为检查、检验、药品、治疗、手术等等多种类型的医嘱。医生开医嘱的过程是一个个开,然后保存,保存的过程称为草稿,可以开很多的草稿,然后一块提交,提交也就是正式生效

二.具体实现

   因为担心医生开立很多草稿一块提交,导致数据量过大,系统响应慢,所以此处使用的多线程提交,多线程的维度是有几种类型的医嘱就开几个线程,同时多线程提交还要考虑事务一致性的问题,所以多线程使用countdownlatch来控制控制线程统一提交或者回滚,系统又使用ThreadLocal存储线程级的共享变量,需要将ThreadLocal传递给子线程

if(sdClinicTypeSet.contains(Constants.SD_CLINIC_TYPE_1)){
    futureList.add(executor.submit(new AsynCallable(requestAttributes, transFlag, transDrug)));
}
if(sdClinicTypeSet.contains(Constants.SD_CLINIC_TYPE_2)){
    futureList.add(executor.submit(new AsynCallable(requestAttributes,transFlag,transExam)));
}
if(sdClinicTypeSet.contains(Constants.SD_CLINIC_TYPE_3)){
    futureList.add(executor.submit(new AsynCallable(requestAttributes,transFlag,transInspect)));
}
if(sdClinicTypeSet.contains(Constants.SD_CLINIC_TYPE_4)){
    futureList.add(executor.submit(new AsynCallable(requestAttributes,transFlag,transTreat)));
}
if(sdClinicTypeSet.contains(Constants.SD_CLINIC_TYPE_11)){
    futureList.add(executor.submit(new AsynCallable(requestAttributes,transFlag,transPathology)));
}
if(sdClinicTypeSet.contains(Constants.SD_CLINIC_TYPE_9)){
    futureList.add(executor.submit(new AsynCallable(requestAttributes,transFlag,transConsultation)));
}

 AsynCallable为自定义线程,trans开头为具体实现类

public class AsynCallable implements Callable, IThreadCacheService {

    //事物执行体,也就是具体医嘱类型
    private AbstractTransService service;
    //事物标志
    private TransFlag transFlag;

    private RequestAttributes requestAttributes;


    public AsynCallable(RequestAttributes parentRequestAttributes, TransFlag transFlag, AbstractTransService service){
        this.service = service;
        this.transFlag = transFlag;
        this.requestAttributes = parentRequestAttributes;

    }
    @Override
    public Object call() throws Exception {
        try {
            RequestContextHolder.setRequestAttributes(this.requestAttributes);
            putDtSign(transFlag.getDtSign());
            Object o = service.lockExec(transFlag,this.service.getClass());
            return o;
        } finally {
            drugItemsTl.remove();
            dtSignThread.remove();
            serialNoThread.remove();
            orderGroupThread.remove();
            RequestContextHolder.resetRequestAttributes();
        }
    }
} 

public abstract class AbstractTransService {

    @Transactional(rollbackFor = Exception.class)
    public T lockExec(TransFlag transFlag,Class clazz) throws ExecutionException, InterruptedException {
        T t = null;
        try {
            t = doExec(transFlag.getIdVisit(),transFlag.getIdDeptSign(),transFlag.getSignDeptName());
        } catch (Exception e) {
            LogUtil.error("-------当前医嘱提交失败---thread:",e);
            transFlag.setAllSuccess(false);
            transFlag.getCountDownLatch().countDown();
            transFlag.getSdClinicTypeClass().add(clazz);
            if(e instanceof ExecutionException){
                ExecutionException ee = (ExecutionException)e;
                if(ee.getCause() instanceof CustomVerify){
                    CustomVerify cv = (CustomVerify)ee.getCause();
                    transFlag.setMsg(cv.getMsg());
                }else{
                    transFlag.setMsg(ee.getMessage());
                }
            }else{
                transFlag.setMsg(e.getMessage());
            }
            LogUtil.error("-------当前医嘱提交失败---thread:["+Thread.currentThread().getName()+"]:"+e.getMessage());
            throw e;
        }

        try {
            transFlag.getCountDownLatch().countDown();
            transFlag.getSdClinicTypeClass().add(clazz);
            transFlag.getCountDownLatch().await();
            if(!transFlag.isAllSuccess()){
                LogUtil.error("--------有未成功提交的医嘱,回滚......");
                throw new CustomVerify(GlobalReturnCode.SYSTEM_PARA_ERROR.getCode(),"提交失败");
            }
        } catch (InterruptedException e) {
            transFlag.setAllSuccess(false);
            LogUtil.error("线程间通讯失败:",e);
            throw new CustomVerify(GlobalReturnCode.SYSTEM_PARA_ERROR.getCode(),"提交失败");
        }
        return t;
    }

    
    public abstract T doExec(String idVisit,String idDeptSign,String signDeptName) throws ExecutionException, InterruptedException;
}

三.问题现象

     在开启多线程的情况偶尔会出现线程不动的情况,之所以说“出现线程不动”,是因为至今也没有找到具体的原因,猜测是数据库死锁导致,但是多线程操作时每个线程只会更新一种类型的医嘱,不会出现多个线程更新同一个记录的情况,这也是至今想不明白的地方,而且测试也无法复现

 图中 58号线程已经不动了,只打出一条update语句,更新就卡住了......

四.后续

     因为这个原因被领导要求反思,最后,只能先由多线程提交改为单线程提交,问题不再出现,但是速度......单线程优化吧!

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号