高并发下Service层的写法
Posted travisgrady
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高并发下Service层的写法相关的知识,希望对你有一定的参考价值。
最近在项目里遇到一个坑,先上简易版的描述:每次从库里查询一下库存余量,每次购买一个商品。
数据库:
store为库存量。
service层代码:
@Override public synchronized void sell() { System.out.println("<======"+System.currentTimeMillis()); //根据局id获取商品信息 Goods goods = goodDao.findOne(1); //获取当前库存 int store = goods.getStore(); System.out.println(Thread.currentThread().getName()+" begin:"+store); if(store - 1 >= 0) { store = store -1; goods.setStore(store); //save当前余量 Goods save = goodDao.save(goods); System.out.println(Thread.currentThread().getName()+" end:"+save.getStore()); } System.out.println(System.currentTimeMillis()+"========>"); }
在这段代码里,因为加了synchronized进行修饰,所以无论多少个线程过来,只会有一个线程对锁住的代码块进行操作,那么,库存始终减1,那么这样是没有问题的。
接下来,如果加入@Transactional,开启声明式事务,那么就会有坑了。
@Override @Transactional public synchronized void sell() { System.out.println("<======"+System.currentTimeMillis()); //根据局id获取商品信息 Goods goods = goodDao.findOne(1);//获取当前库存 int store = goods.getStore(); System.out.println(Thread.currentThread().getName()+" begin:"+store); if(store - 1 >= 0) { store = store -1; goods.setStore(store); //展示库存-1后的余量 Goods save = goodDao.save(goods); //TODO 可能对其他表进行了操作.... System.out.println(Thread.currentThread().getName()+" end:"+save.getStore()); } System.out.println(System.currentTimeMillis()+"========>"); }
由于加入了@Transactional,那么就会当做一个事务来进行处理。如果并发的去执行,那么会库存扣减不一致。原因在于,第一个线程执行完成以后,aop的方法还在继续,需要去commit,这个需要一定的时间。然后这个时候代码块已经走完了,释放了锁,那下一个线程过来去库里查,还是commit前的库存数量,所以,导致该问题。
解决办法是自定义一个查询方法,使用select ... for update的方式,给这条数据加上锁。JPA的repositry里的写法:
//PESSIMISTIC_WRITE:事务开始即获得数据库的锁 @Lock(value=LockModeType.PESSIMISTIC_WRITE) @Query(value = "select t from Goods t where t.id =?1 ") Goods queryById(Integer id);
那么就ok了,原理是这样的: 在第一个线程进来的时候,开启了一个事务,给当前这行数据加了一个行锁,然后在代码执行到最后的时候,虽然jvm里的锁会释放,第二个线程会进来,但是会卡在select for update这里,因为第一个事务还没有提交,所以行锁还在。直到第一个事务提交了以后,第二个线程才会继续执行,查询到数据,这个时候的数据,一定是commit完成以后的数据了。那就不会有脏数据的发生。
这次问题的主要原因是JVM锁与@Transactional声明式事务aop没法同时执行的原因导致的。所以使用编程式事务是不存在上述问题的(我试过)。
以上是关于高并发下Service层的写法的主要内容,如果未能解决你的问题,请参考以下文章