遇到线程安全问题的案例

web项目中每个请求都是一个新的线程,如下代码,在不同的service层中操作相同的数据时,会出现线程安全问题。具体表现是:设定 sms初始值为10,smsUsed初始值为0;则两个请求分别同时访问test方法和testB方法时,两个方法中输出的结果显示:sms:9 smsUsed:1;按照正常的业务逻辑来说,当两个请求都完成后,sms应该为8,smsUsed为2。

@Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void test(){
        Long hid = 6L;
        Saler saler = salerDAO.getSalerBySid(hid);
        Long sms = saler.getSmsUnused();
        sms--;
        Long smsUsed = saler.getSmsUsed();
        smsUsed++;
        System.out.println("test ##### sms:"+ sms +" smsUsed:" + smsUsed);
        try {
            Thread.sleep(15000);
        }catch (Exception e) {
            throw new SelectNoFindException("test睡眠报错了",null);
        }
        salerDAO.updateSalerSms(hid, sms, smsUsed);
        System.out.println("test ##### sms:"+ sms +" smsUsed:" + smsUsed);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public void testB(){
        Long hid = 6L;
        Saler saler = salerDAO.getSalerBySid(hid);
        Long sms = saler.getSmsUnused();
        sms--;
        Long smsUsed = saler.getSmsUsed();
        smsUsed++;
        salerDAO.updateSalerSms(hid, sms, smsUsed);
        System.out.println("testB ##### sms:"+ sms +" smsUsed:" + smsUsed);
    }
View Code

解决方案: 

  方案1.将两个方法合并成一个,在获取和修改共享数据时加锁。

  方案2.因为两个方法中修改共享数据的sql是相同的,所以可以使用数据库的行级锁(排它锁),在查询共享数据的sql语句后面加上 “for update”;

猜你喜欢

转载自www.cnblogs.com/zhlblogs/p/12674709.html
今日推荐