目录
一.业务背景
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
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语句,更新就卡住了......
四.后续
因为这个原因被领导要求反思,最后,只能先由多线程提交改为单线程提交,问题不再出现,但是速度......单线程优化吧!



