遇到线程安全问题的案例
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); }
解决方案:
方案1.将两个方法合并成一个,在获取和修改共享数据时加锁。
方案2.因为两个方法中修改共享数据的sql是相同的,所以可以使用数据库的行级锁(排它锁),在查询共享数据的sql语句后面加上 “for update”;