项目遇到的多线程的并发问题和分析

这里先简单描述一下问题,

场景:在一个线程中创建了另外一个线程处理一部分业务,

主线程对数据库进行新增操作,在操作结束后,启动一个新的线程去执行查询插入的数据的查询操作,发现了数据没有查询的问题。

下面我们我们直接上代码:

@Service
@Transactional
@Slf4j
public class RPatientService extends BaseService {

    @Autowired
    private SequenceIdGenerator sequenceIdGenerator;

    @Autowired
    private WeChatMessageService weChatMessageService;

    @Autowired
    private RPatientMapper rPatientMapper;

    @Autowired
    private RReportIllnessService rReportIllnessService;
    @Autowired
    private ImSessionUserService imSessionUserService;

    @Autowired
    private RDoctorService rDoctorService;

    
    public void registerPS(String openId, String doctorId, byte status) {
        //按openId查询用户
        synchronized (openId.intern()) {
            RUser rUser = rUserService.getRUserByOpenId(openId);
            RPatient rPatient = null;
            if (rUser == null) {
                //用户不存在插入用户
                rUser = rUserService.createUserByOpenIdAndPS(openId, Constant.RUSER_TYPE_0_PATIENT, status);
                //新增患者
                rPatient = initPatient(rUser);
                log.info("[RPatientService]:new patient join:{}", JsonUtil.toJson(rUser));
            } else if (rUser.getType() == Constant.RUSER_TYPE_1_DOCTOR) {
                //医生扫描医生二维码,不处理
                return;
            }

            if (rPatient == null) {
                rPatient = this.getPatientByUserId(rUser.getId());
                if(rPatient == null){
                    log.error("[RPatientService]: patient not exist:{}", JsonUtil.toJson(rUser));
                    return ;
                }
            }

            Long rDoctorId = null;
            if (!StringUtils.isEmpty(doctorId)) {
                //报到   关联医生和患者生成report
                rDoctorId = Long.valueOf(doctorId);
                RDoctor rDoctor = rDoctorService.getDoctorById(rDoctorId);
                if(rDoctor == null){
                    log.error("[RPatientService]: doctor not exist:doctorId:{}", doctorId);
                    return;
                }
                rReportService.report(rDoctorId, rPatient.getId());
            }
            eventBus.post(new PatientEvent(rUser.getId(), rDoctorId));
            //新
            //this.registerEvent(new PatientEvent(rUser.getId(), rDoctorId));
        }
    }

    //EventBus执行
    @Subscribe
    @AllowConcurrentEvents
    public void registerEvent(PatientEvent patientEvent) {
        log.info("[PatientEvent]:{}", JsonUtil.toJson(patientEvent));
        Long rUserId = patientEvent.getrUserId();
        if (rUserId == null) {
            return;
        }
        RUser rUser = rUserService.selectByPrimaryKey(rUserId);
        RPatient rPatient = this.getPatientByUserId(rUserId);
//        RReport rReport = rReportService.getByPatientId(rPatient.getId());
        if (StringUtils.isEmpty(rPatient.getPhone())) {
            //提示用户身份验证
            weChatMessageService.sendToIdentityVerificationKFMsgForPatient(rUser.getVxopenid());
        } else if (patientEvent.getrDoctorId() != null) {

            //创建会话
            rReportService.createNewSession(rPatient.getId());

            //已关联医生,提示选择疾病
            weChatMessageService.sendToChoiceIllnessKFMsgForPatient(rUser.getVxopenid(), patientEvent.getrDoctorId());

        }
    }



}

 

新增操作的代码:

@Service
@Transactional
@Slf4j
public class RReportService extends BaseService {

    @Autowired
    private SequenceIdGenerator sequenceIdGenerator;
    @Autowired
    private RReportMapper rReportMapper;

    @Autowired
    private RPatientService patientService;

    @Autowired
    private ImSessionService imSessionService;

    public RReport report(Long doctorId, Long patientId) {
        RReport rReport = getByDoctorIdPatientId(doctorId, patientId);
        if (rReport == null) {
            rReport = initRReport(doctorId, patientId);
            log.info("new report generate:{}", JsonUtil.toJson(rReport));
        }
        return rReport;
    }

    //插入数据
    private RReport initRReport(Long doctorId, Long patientId) {
        RReport rReport = new RReport();
        rReport.setRdoctorid(doctorId);
        rReport.setRpatientid(patientId);
        rReport.setCreatetime(new Date());
        insertOrUpdate(rReport);
        return rReport;
    }

    
    //EventBus新事务执行的代码
    public void createNewSession(Long patientId) {
        RPatient rPatient = patientService.selectByPrimaryKey(patientId);
        if (StringUtils.isEmpty(rPatient.getPhone())) {
            log.info("患者没有完善个人信息,需要先完善个人信息!");
            return;//未完善信息的不处理
        }
        RReportExample rReportExample = new RReportExample();
        rReportExample.createCriteria().andRpatientidEqualTo(patientId).andImsessionidIsNull();
        List rReportList = this.selectByExample(rReportExample);
        log.info("查询隐患报道关系." + "数量只能有一条:" + rReportList.size() + "记录信息=" + rReportList.toString());
        if (rReportList != null && rReportList.size() != 0) {
            for (RReport rReport : rReportList) {
                Long doctorId = rReport.getRdoctorid();
                //创建会话
                ImSession imSession = imSessionService.createNewSession(doctorId, patientId);
                rReport.setImsessionid(imSession.getId());
                this.insertOrUpdate(rReport);//更新会话ID
            }
        }
        ImSessionExample imSessionExample = new ImSessionExample();
        imSessionExample.createCriteria().andCreateuseridEqualTo(patientId).andSessiontypeEqualTo((byte) 2);
        ImSession a = imSessionService.selectOneByExample(imSessionExample);
        if (a == null) {
            //创建客服
            imSessionService.createNewSession2(424987751667077198L, patientId);
        }
    }

}

 

 

下面直接看我分析服务器打出一段日志就可以了

############这里创建了一个事务	35ea154e		http-nio-8081-exec-10线程编号
2020-01-11 11:13:47.834 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Creating new transaction with name [com.framework.code.service.report.RPatientService.registerPS]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT
2020-01-11 11:13:47.836 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]] for JDBC transaction
########切换JDBC事务并且交给Spring管理
2020-01-11 11:13:47.837 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Switching JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]] to manual commit
############参与当前的事务	35ea154e
2020-01-11 11:13:47.837 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Participating in existing transaction
2020-01-11 11:13:47.838 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.doctor.RUserMapper.selectByExample | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]]
#######通过openid查询用户
2020-01-11 11:13:47.838 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.doctor.RUserMapper.selectByExample | ==>  Preparing: select 'true' as QUERYID, Id_, VxHeadUrl_, VxNickName_, VxSex_, VxOpenid_, VxProvince_, VxCity_, VxCountry_, Type_, PromotionSource_ from r_user WHERE ( VxOpenid_ = ? ) 
2020-01-11 11:13:47.839 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.doctor.RUserMapper.selectByExample | ==> Parameters: oMGuv5uRcp03ycIgyjclwpZfr1Eg(String)
##########使用当前事务35ea154e
2020-01-11 11:13:47.842 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RPatientMapper.selectByExample | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]]
#######根据用户Id查询患者信息
2020-01-11 11:13:47.842 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RPatientMapper.selectByExample | ==>  Preparing: select 'true' as QUERYID, Id_, RUserId_, RegisterTime_, Name_, Phone_, CardNo_, Age_, Illness_, Irritability_, Inheritance_, IsBindFinished_, Sex_, VxHeadUrl_, PfksShopUserId_ from r_patient WHERE ( RUserId_ = ? ) 
2020-01-11 11:13:47.843 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RPatientMapper.selectByExample | ==> Parameters: 458690345937539120(Long)
#############这里表示使用AOP调用其他的事务,判断当前有事务,那么默认直接使用当前事务
2020-01-11 11:13:47.844 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Participating in existing transaction
############继续使用当前事务	35ea154e
2020-01-11 11:13:47.845 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.doctor.RDoctorMapper.selectByExample | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]]
#######通过医生Id查询医生信息
2020-01-11 11:13:47.845 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.doctor.RDoctorMapper.selectByExample | ==>  Preparing: select 'true' as QUERYID, Id_, RUserId_, Name_, NamePY_, RHospitalId_, RDepartmentId_, PositionCode_, Phone_, RegisterTime_, CheckStatus_, CheckTime_, CheckUserName_, CheckRemark_, Speciality_, Profile_, VxHeadUrl_, Dtype_, PfksShopUserId_, RDocCareType_ from r_doctor WHERE ( Id_ = ? ) 
2020-01-11 11:13:47.846 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.doctor.RDoctorMapper.selectByExample | ==> Parameters: 463413442003472478(Long)

#############再次使用AOP调用其他的事务,判断当前有事务,那么默认使用当前事务
2020-01-11 11:13:47.849 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Participating in existing transaction
#########继续使用当前事务	35ea154e
2020-01-11 11:13:47.851 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RReportMapper.selectByExample | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]]

#########通过医生ID和患者的ID查询医患报道表的信息
2020-01-11 11:13:47.852 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RReportMapper.selectByExample | ==>  Preparing: select 'true' as QUERYID, Id_, RPatientId_, RDoctorId_, CreateTime_, IMSessionId_, IllnessName_, RDoctorNameRemark_, RPatientNameRemark_ from r_report WHERE ( RDoctorId_ = ? and RPatientId_ = ? ) 
2020-01-11 11:13:47.853 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RReportMapper.selectByExample | ==> Parameters: 463413442003472478(Long), 458690345958510641(Long)
##############继续使用当前事务	35ea154e
2020-01-11 11:13:47.854 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RReportMapper.insert | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]]
#############添加医患报道表的一条记录		患者ID=458690345958510641	IMSessionId_是null	
2020-01-11 11:13:47.857 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RReportMapper.insert | ==>  Preparing: insert into r_report (Id_, RPatientId_, RDoctorId_, CreateTime_, IMSessionId_, IllnessName_, RDoctorNameRemark_, RPatientNameRemark_) values (?, ?, ?, ?, ?, ?, ?, ?) 
2020-01-11 11:13:47.858 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.mapper.report.RReportMapper.insert | ==> Parameters: 481834561777569793(Long), 458690345958510641(Long), 463413442003472478(Long), 2020-01-11 11:13:47.853(Timestamp), null, null, null, null

###########这里输出的日志
2020-01-11 11:13:47.861 | myHost | INFO  | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | com.framework.code.service.report.RReportService | new report generate:{"id":481834561777569793,"rpatientid":458690345958510641,"rdoctorid":463413442003472478,"createtime":1578712427853,"imsessionid":null,"illnessname":null,"rdoctornameremark":null,"rpatientnameremark":null}

########提交启动的事务
2020-01-11 11:13:47.861 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Initiating transaction commit


#########这里我们又需要创建一个新的事务		7fb1091a		线程编号pool-1-thread-7
2020-01-11 11:13:47.861 | myHost | DEBUG | pool-1-thread-7 | [] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Creating new transaction with name [com.framework.code.service.report.RPatientService.registerEvent]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT

########之前线程的事务进行提交
2020-01-11 11:13:47.861 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Committing JDBC transaction on Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]]

################这里是我们新的事务			7fb1091a
2020-01-11 11:13:47.862 | myHost | DEBUG | pool-1-thread-7 | [] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Acquired Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@75935acf [wrapping: com.mysql.jdbc.JDBC4Connection@7fb1091a]] for JDBC transaction
2020-01-11 11:13:47.862 | myHost | DEBUG | pool-1-thread-7 | [] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Switching JDBC Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@75935acf [wrapping: com.mysql.jdbc.JDBC4Connection@7fb1091a]] to manual commit

######这里是日志输出
2020-01-11 11:13:47.862 | myHost | INFO  | pool-1-thread-7 | [] | com.framework.code.service.report.RPatientService | [PatientEvent]:{"rUserId":458690345937539120,"rDoctorId":463413442003472478}

##########使用当前事务
2020-01-11 11:13:47.862 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.doctor.RUserMapper.selectByPrimaryKey | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@75935acf [wrapping: com.mysql.jdbc.JDBC4Connection@7fb1091a]]

##########通过用户Id查询用户信息
2020-01-11 11:13:47.862 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.doctor.RUserMapper.selectByPrimaryKey | ==>  Preparing: select Id_, VxHeadUrl_, VxNickName_, VxSex_, VxOpenid_, VxProvince_, VxCity_, VxCountry_, Type_, PromotionSource_ from r_user where Id_ = ? 
2020-01-11 11:13:47.863 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.doctor.RUserMapper.selectByPrimaryKey | ==> Parameters: 458690345937539120(Long)


######之前的线程释放JDBC连接
2020-01-11 11:13:47.867 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Releasing JDBC Connection
[com.mchange.v2.c3p0.impl.NewProxyConnection@5c225ae5 [wrapping: com.mysql.jdbc.JDBC4Connection@35ea154e]] after transaction
2020-01-11 11:13:47.867 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.jdbc.datasource.DataSourceUtils | Returning JDBC Connection to DataSource
2020-01-11 11:13:47.867 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.web.servlet.DispatcherServlet | Completed 200 OK


####被AOP的切面拦截输入的日志
2020-01-11 11:13:47.867 | myHost | INFO  | pool-1-thread-8 | [] | com.framework.web.eventbus.LogSubscribe | [LogSubscribe]:{"threadName":"http-nio-8081-exec-10","className":"com.framework.code.controller.webchat.WeChartController","methodName":"receiveEvent","argsMap":{"request":"ServletRequest","response":"ServletResponse"},"argsJson":"{\"request\":\"ServletRequest\",\"response\":\"ServletResponse\"}","happenTime":1578712427867,"throwable":null,"throwableString":null,"traceId":"436ed24f998d411ab5150b1258b22cd0","result":null,"useTime":33}

2020-01-11 11:13:47.867 | myHost | DEBUG | http-nio-8081-exec-10 | [436ed24f998d411ab5150b1258b22cd0] | org.springframework.session.web.http.SessionRepositoryFilter.SESSION_LOGGER | No session found by id: Caching result for getSession(false) for this HttpServletRequest.



########使用当前事务 			7fb1091a
2020-01-11 11:13:47.867 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RPatientMapper.selectByExample | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@75935acf [wrapping: com.mysql.jdbc.JDBC4Connection@7fb1091a]]

###通过用户ID查询患者信息
2020-01-11 11:13:47.867 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RPatientMapper.selectByExample | ==>  Preparing: select 'true' as QUERYID, Id_, RUserId_, RegisterTime_, Name_, Phone_, CardNo_, Age_, Illness_, Irritability_, Inheritance_, IsBindFinished_, Sex_, VxHeadUrl_, PfksShopUserId_ from r_patient WHERE ( RUserId_ = ? ) 
2020-01-11 11:13:47.869 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RPatientMapper.selectByExample | ==> Parameters: 458690345937539120(Long)


##########使用AOP调用其他的事务,判断当前有事务,那么默认使用当前事务		7fb1091a
2020-01-11 11:13:47.870 | myHost | DEBUG | pool-1-thread-7 | [] | org.springframework.jdbc.datasource.DataSourceTransactionManager | Participating in existing transaction
2020-01-11 11:13:47.876 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RPatientMapper.selectByPrimaryKey | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@75935acf [wrapping: com.mysql.jdbc.JDBC4Connection@7fb1091a]]

######通过用户ID查询患者信息
2020-01-11 11:13:47.876 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RPatientMapper.selectByPrimaryKey | ==>  Preparing: select Id_, RUserId_, RegisterTime_, Name_, Phone_, CardNo_, Age_, Illness_, Irritability_, Inheritance_, IsBindFinished_, Sex_, VxHeadUrl_, PfksShopUserId_ from r_patient where Id_ = ? 
2020-01-11 11:13:47.876 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RPatientMapper.selectByPrimaryKey | ==> Parameters: 458690345958510641(Long)

##########使用当前事务		7fb1091a
2020-01-11 11:13:47.878 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RReportMapper.selectByExample | ooo Using Connection [com.mchange.v2.c3p0.impl.NewProxyConnection@75935acf [wrapping: com.mysql.jdbc.JDBC4Connection@7fb1091a]]
##########通过患者ID查询患者报道信息表		458690345958510641
2020-01-11 11:13:47.878 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RReportMapper.selectByExample | ==>  Preparing: select 'true' as QUERYID, Id_, RPatientId_, RDoctorId_, CreateTime_, IMSessionId_, IllnessName_, RDoctorNameRemark_, RPatientNameRemark_ from r_report WHERE ( RPatientId_ = ? and IMSessionId_ is null ) 
2020-01-11 11:13:47.878 | myHost | DEBUG | pool-1-thread-7 | [] | com.framework.code.mapper.report.RReportMapper.selectByExample | ==> Parameters: 458690345958510641(Long)

########输出查询结果 
2020-01-11 11:13:47.879 | myHost | INFO  | pool-1-thread-7 | [] | com.framework.code.service.report.RReportService | 查询隐患报道关系.数量只能有一条:0记录信息=[]

 

 

看完日志 我们不难发现,在主线程明明已经执行完成新增操作,并且已经释放了JDBC连接,在EventBus的线程中新的事务中并没有查询到我们要插入的数据。。。。

按照正常的逻辑思考这就是一个死循环的。。。。。。明明我已经执行完成了新增操作并且释放了连接到连接池 。。问什么我在新的事务中查询不到数据呢。

 

个人的分析可能数据库出来也许一个短暂 的时间,因为主线程插入数据的时间和EventBus线程去查询间隔时间只有0.02秒的时间可能数据并没有写入完成操作的。我的解决方案就是放弃EventBus使用主线程一次处理所有业务,让一次接口调用的响应时间加长,来减少并发出现的问题。

你可能感兴趣的:(java学习,多线下的并发问题)