一:
先上结论,如果把锁放在事务里面,会出现脏读数据,解决方案:锁上移。
1.有问题的代码:
@Service public class SeckillServiceImpl extends ServiceImpl<SeckillMapper, Seckill> implements ISeckillService { private static Lock lock = new ReentrantLock(true); @Autowired private ISuccessKilledService successKilledService; @Override @Transactional public String startKill(String id) { lock.lock(); try { Seckill seckill = this.getOne(new LambdaQueryWrapper<Seckill>().eq(Seckill::getSeckillId, id)); int number = seckill.getNumber(); if (number > 0) { Seckill seckill1 = seckill.setNumber(--number); this.update(seckill1, new LambdaQueryWrapper<Seckill>().eq(Seckill::getSeckillId, seckill.getSeckillId())); SuccessKilled successKilled = new SuccessKilled(); successKilled.setUserId(1L); successKilled.setSeckillId(seckill.getSeckillId()); successKilled.setState(0); successKilled.setCreateTime(new Date()); successKilledService.save(successKilled); } } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } return "success"; } }
本例子中锁在事务里面,并发时就会出现锁已经解锁了单事务没有提交,另一个线程读到了没有 提交之前的数据。
解决方案:
AOP+锁。
@Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Servicelock { String description() default ""; }
@Component @Aspect public class LockAspect { private static Lock lock = new ReentrantLock(true);//互斥锁 参数默认false,不公平锁 @Pointcut("@annotation(com.example.demo.Servicelock)") public void lockAspect() { } @Around("lockAspect()") public Object around(ProceedingJoinPoint joinPoint) { lock.lock(); Object obj = null; try { obj = joinPoint.proceed(); } catch (Throwable e) { e.printStackTrace(); } finally { lock.unlock(); } return obj; } }
注意:通知类型一定要是环绕通知。
@RequestMapping("start") @ResponseBody @Servicelock public String start(String seckillId) { return killService.startKill(seckillId); }