商城网站建设要多少钱,51简历模板网,做一个网站设计要多少钱,门户网站开发的价格我们之前讲了秒杀模块的实现#xff0c;使用了sychronized互斥锁#xff0c;但是在集群模式下因为不同服务器有不同jvm#xff0c;所以synchronized互斥锁失效了。
redis实现秒杀超卖问题的解决方案#xff1a;(仅限于单体项目)-CSDN博客
这时就要找到一个多台服务器都能…我们之前讲了秒杀模块的实现使用了sychronized互斥锁但是在集群模式下因为不同服务器有不同jvm所以synchronized互斥锁失效了。
redis实现秒杀超卖问题的解决方案(仅限于单体项目)-CSDN博客
这时就要找到一个多台服务器都能识别的锁即redis中的setNX充当互斥锁来控制秒杀的一人一单
在redis缓存击穿中使用逻辑过期就用过互斥锁这里原理一摸一样只不过这里存储的value为UUID线程ID
setNX互斥锁的使用
场景1会导致一个用户创建多个订单 注*线程1和线程2的userID相同所以创建的redis锁key值相同但是value不相同释放锁时如果不进行验证value值很有可能会出现场景1的情况。
场景2 注*在线程1检查锁后发现自己的锁过期了该锁不是自己创建的说明其他相同userID的线程也在创建订单这时应该回滚撤销之前数据库操作。(在调用减库存创建订单的方法中回滚) 代码实现
锁工具
创建后不能注册为Bean用的时候new对象即可如果想注册为Bean使用keywords和value都要作为参数传递否者会出现多线程随意修改该值的情况
public class RedisLock {StringRedisTemplate template;String keywords;String value;public RedisLock(StringRedisTemplate template,String keywords){this.keywordskeywords;this.templatetemplate;}//尝试创建锁public boolean tryLock(Integer timeOutSecond){//给该线程生成唯一标识作为valuevalueUUID.randomUUID().toString().replace(-,)-Thread.currentThread().getId();return template.opsForValue().setIfAbsent(lock:keywords,value,timeOutSecond, TimeUnit.SECONDS);}//尝试删除锁public void delLovk(){//释放锁之前先验证锁是否过期是为自己的锁String result template.opsForValue().get(lock: keywords);if(result!null || result.equals(value)){template.delete(lock:keywords);}}
}
之所以不将该类注册为bean使用是因为创建锁时要获取UUID线程的ID删除锁时也需要该值所以这个值只能使用一个全局变量来记录。
如果注册为bean后所有线程的操作都使用该对象中value属性去进行赋值和删除操作就会导致value被不断修改keywords也会被不断修改最终导致程序逻辑错误应该一个线程使用一个独有的value属性所以不能将该工具类注册为Bean用的时候new即可 业务逻辑代码有原来的synchronized改为分布式锁控制线程创建订单
AutowiredApplicationContext context;//模仿秒杀减库存创建订单Overridepublic Boolean killInSecond(Integer userID,Integer productID){//检查库存是否0Product product pm.selectByPrimaryKey(productID);if(product.getSales()0){throw new MyExceptionHandler(库存不足);}//调用2-4步骤方法Boolean resultfalse;//同步锁
// synchronized (userID.toString().intern()){
// //使用代理对象调用事务方法
// ProductServiceImpl bean context.getBean(ProductServiceImpl.class);
// resultbean.ProductAndOrder(userID,productID);
// }//分布式锁RedisLock redisLock new RedisLock(template, order: userID);//获取锁resultredisLock.tryLock(30);if(!result){throw new RuntimeException(该用户只能创建一个订单);}//使用代理对象调用事务方法ProductServiceImpl bean context.getBean(ProductServiceImpl.class);resultbean.ProductAndOrder(userID,productID);//释放锁redisLock.delLovk();return result;}
减库存创建订单方法 AutowiredOrderMapper om;AutowiredRedisIdIncrement redisId;//创建订单减库存操作Transactionalpublic Boolean ProductAndOrder(Integer userID,Integer productID){//检查数据库中书否存在该用户订单Integer orderCount om.selectOrderByUserIdAndProductId(userID, productID);if(orderCount0){throw new MyExceptionHandler(用户已下单);}//订单不存在减库存宽松乐观锁Integer result pm.updateProductBysale(productID);if(result!1){throw new MyExceptionHandler(库存不足);}//创建订单//获取redis唯一IDLong orderId redisId.getRedisID(order);//封装订单Order ordernew Order(orderId.toString(),userID,,,productID,,null,1,0,null,null,null,null,new BigDecimal(100));result om.insertCompleteOrder(order);if(result!1){return false;}return true;}
不足 虽然我们通过检查锁的value值判断该锁是否为本线程创建的锁控制了误删锁的可能但是这里依然会没有解决多个相同userID的线程会创建多个订单的情况。
情况一
在线程1检查锁后发现自己的锁过期了该锁不是自己创建的说明其他相同userID的线程也在创建订单这时应该回滚撤销之前数据库操作。(在调用减库存创建订单的方法中回滚) 情况2
删除锁时发现自己的锁过期了缓存中没有该锁说明
1.没有其他相同userID用户执行创建订单的逻辑不需回滚直接结束程序即可 2.有其他线程执行了操作但是已经执行完毕订单也已经创建完毕继续执行程序即可因为创建订单时发现订单已存在自会回滚