@Service("seckillService") public class SeckillServiceImpl implements ISeckillService { /** * 堆代码 duidaima.com * 思考:为什么不用synchronized * service 默认是单例的,并发下lock只有一个实例 */ private Lock lock = new ReentrantLock(true);//互斥锁 参数默认false,不公平锁 @Autowired private DynamicQuery dynamicQuery; @Override @Transactional public Result startSeckilLock(long seckillId, long userId) { try { lock.lock(); //这里、不清楚为啥、总是会被超卖101、难道锁不起作用、lock是同一个对象 String nativeSql = "SELECT number FROM seckill WHERE seckill_id=?"; Object object = dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId}); Long number = ((Number) object).longValue(); if(number>0){ nativeSql = "UPDATE seckill SET number=number-1 WHERE seckill_id=?"; dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId}); SuccessKilled killed = new SuccessKilled(); killed.setSeckillId(seckillId); killed.setUserId(userId); killed.setState(Short.parseShort(number+"")); killed.setCreateTime(new Timestamp(new Date().getTime())); dynamicQuery.save(killed); }else{ return Result.error(SeckillStatEnum.END); } } catch (Exception e) { e.printStackTrace(); }finally { lock.unlock(); } return Result.ok(SeckillStatEnum.SUCCESS); } }代码写在service层,bean默认是单例的,也就是说lock肯定是一个对象。感觉不放心,还是打印一下 lock.hashCode(),输出结果没问题。由于还有其他事情要做,最终还是带着疑问提交代码到码云。
@Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface Servicelock { String description() default ""; }自定义切面LockAspect:
@Component @Scope @Aspect public class LockAspect { /** * 堆代码 duidaima.com * service 默认是单例的,并发下lock只有一个实例 */ private static Lock lock = new ReentrantLock(true);//互斥锁 参数默认false,不公平锁 //Service层切点 用于记录错误日志 @Pointcut("@annotation(com.itstyle.seckill.common.aop.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; } }
切入秒杀方法:
@Service("seckillService") public class SeckillServiceImpl implements ISeckillService { /** * 堆代码 duidaima.com * service 默认是单例的,并发下lock只有一个实例 */ private Lock lock = new ReentrantLock(true);//互斥锁 参数默认false,不公平锁 @Autowired private DynamicQuery dynamicQuery; @Override @Servicelock @Transactional public Result startSeckilAopLock(long seckillId, long userId) { //来自码云码友<马丁的早晨>的建议 使用AOP + 锁实现 String nativeSql = "SELECT number FROM seckill WHERE seckill_id=?"; Object object = dynamicQuery.nativeQueryObject(nativeSql, new Object[]{seckillId}); Long number = ((Number) object).longValue(); if(number>0){ nativeSql = "UPDATE seckill SET number=number-1 WHERE seckill_id=?"; dynamicQuery.nativeExecuteUpdate(nativeSql, new Object[]{seckillId}); SuccessKilled killed = new SuccessKilled(); killed.setSeckillId(seckillId); killed.setUserId(userId); killed.setState(Short.parseShort(number+"")); killed.setCreateTime(new Timestamp(new Date().getTime())); dynamicQuery.save(killed); }else{ return Result.error(SeckillStatEnum.END); } return Result.ok(SeckillStatEnum.SUCCESS); } }所有的工作完成以后,我们来测试一下代码,意料之中,再也没有出现超卖的现象。然而,你以为就这么结束了么?细心的码友IM核米,又提出了以下问题:Spring 里的切片在未指定排序的时候,两个注解是随意执行的。如果事务在加锁前执行的话,是不是就会产生问题?