先看一段代码:代码逻辑很简单,当用户请求购买商品时我们去后台查找商品库存如果库存大于购买数量我们就更新库存。显然查找跟更新是一个事物所以我们要加@Transactional,同时为了防止高并发下多个用户同一时间去扣减库存也就是多个用户同时执行该方法就需要对方法加锁synchronized。看起来这段代码好像没啥问题,但是当你运行起来之后你会发现出现库存超扣现象
@Transactional ( rollbackFor = Exception . class )
public synchronized Integer createOrder ( ) throws Exception {
Product product = productMapper. selectByPrimaryKey ( purchaseProductId) ;
if ( product== null ) {
throw new Exception ( "购买商品:" + purchaseProductId+ "不存在" ) ;
}
Integer currentCount = product. getCount ( ) ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + "库存数:" + currentCount) ;
if ( purchaseProductNum > currentCount) {
throw new Exception ( "商品" + purchaseProductId+ "仅剩" + currentCount+ "件,无法购买" ) ;
}
productMapper. updateProductCount ( purchaseProductNum, "xxx" , new Date ( ) , product. getId ( ) ) ;
TransactionStatus transaction = platformTransactionManager. getTransaction ( transactionDefinition) ;
Order order = new Order ( ) ;
order. setOrderAmount ( product. getPrice ( ) . multiply ( new BigDecimal ( purchaseProductNum) ) ) ;
order. setOrderStatus ( 1 ) ;
order. setReceiverName ( "xxx" ) ;
orderMapper. insertSelective ( order) ;
OrderItem orderItem = new OrderItem ( ) ;
orderItem. setOrderId ( order. getId ( ) ) ;
orderItemMapper. insertSelective ( orderItem) ;
return order. getId ( ) ;
}
原因分析
@Transactional是通过aop的方式执行的,由于spring的aop,会在createOrder方法之前开启事务,之后再加锁,当锁住的代码执行完成后,再提交事务,因此锁住的代码块执行是在事务之内执行的,可以推断在代码块执行完时,事务还未提交,锁已经被释放,此时其他线程拿到锁之后进行锁住的代码块,读取的库存数据不是最新的
解决办法
通过分析我们知道,之所以会这样是应为事物并没有被锁住,所以要想达到想过我们必须先加锁在让事物生效,参考代码如下:
public synchronized Integer createOrder ( ) throws Exception {
TransactionStatus transaction1 = platformTransactionManager. getTransaction ( transactionDefinition) ;
Product product = productMapper. selectByPrimaryKey ( purchaseProductId) ;
if ( product== null ) {
platformTransactionManager. rollback ( transaction1) ;
throw new Exception ( "购买商品:" + purchaseProductId+ "不存在" ) ;
}
Integer currentCount = product. getCount ( ) ;
System . out. println ( Thread . currentThread ( ) . getName ( ) + "库存数:" + currentCount) ;
if ( purchaseProductNum > currentCount) {
platformTransactionManager. rollback ( transaction1) ;
throw new Exception ( "商品" + purchaseProductId+ "仅剩" + currentCount+ "件,无法购买" ) ;
}
productMapper. updateProductCount ( purchaseProductNum, "xxx" , new Date ( ) , product. getId ( ) ) ;
platformTransactionManager. commit ( transaction1) ;
TransactionStatus transaction = platformTransactionManager. getTransaction ( transactionDefinition) ;
Order order = new Order ( ) ;
order. setOrderAmount ( product. getPrice ( ) . multiply ( new BigDecimal ( purchaseProductNum) ) ) ;
order. setOrderStatus ( 1 ) ;
orderMapper. insertSelective ( order) ;
OrderItem orderItem = new OrderItem ( ) ;
orderItem. setOrderId ( order. getId ( ) ) ;
platformTransactionManager. commit ( transaction) ;
return order. getId ( ) ;
}