前面的文章说过,分布式锁的出现是为了解决跨JVM,多台服务器共享一个资源的问题,避免并发出现数据不安全,数据不一致的问题。 本章我们讲述的分布式锁是使用Redisson实现的,其提供了分布式锁的功能组件,可以用来弥补基于 Redis的原子性操作的缺陷,下面分析基于Redis实现分布式锁的不足之处。 比如,如果redis负责存储分布式锁的节点发生了宕机的情况 ,但是该锁又是处于被锁住的状态,这种情况就会出现死锁。为了避免这种情况,Redisson提供了个监控锁的“看门狗”,作用是在Redisson实例被关闭之前,不断延长分布式锁的有效期,默认情况下,这个时间 是30秒,当然我们可以通过Config.lockWarchDogTimeout进行设置。 Redisson为我们提供了多个同业务场景的锁,根据功能特性,我们可以分成可重入锁,公平锁,联锁,红锁,读写锁,**信号量 **,闭锁。 下面介绍可重入锁
这种一次性锁是在某一个时间段内,只能允许一个线程共享资源,其他的线程统统打回,这种业务场景适用于,“用户注册 ”,“抢红包”,“重复提交”,下面我们验证用于 “重复提交”
@RequestMapping("/testRedissonOnceLock") public ResultVo testRedissonOnceLock(@RequestBody UserLoginDto userLoginDto) { // 定义锁的名字 String key = "redisson:myOnceLock" + userLoginDto.getUserName(); // 获取分布式锁(一次性的) RLock rLock = redissonClient.getLock(key); // 一次性锁 rLock.lock(1L,TimeUnit.SECONDS); // 根据 用户名称查询账号 UserReg userReg = userRegMapper.selectByUserName(userLoginDto.getUserName()); if (userReg == null) { UserReg insertUserReg = new UserReg(){ { setCreateTime(new Date()); setPassword(userLoginDto.getPassword()); setUserName(userLoginDto.getUserName()); } }; userRegMapper.insertSelective(insertUserReg); log.info("{}注册成功",userLoginDto.getUserName()); return ResultVo.success("注册成功"); } rLock.forceUnlock(); return ResultVo.success(); }
可重入锁就是当线程 获取不到锁 的时候会等待一段时间,重新去尝试获取锁,如果不能获取锁 ,并且重试的时间达到了上限,则 意味着 该线程就会 被抛弃 。 这种场景适用于同一时刻并发产生很多线程,但是同一时刻不能获取到分布式锁,但是却允许隔一定的时间后重新获取到,典型的应用场景就是商城高并发抢购,比如防止超库。
@RequestMapping("/testReplyRedissonLock") public ResultVo testReplyRedissonLock(@RequestBody BookRobDto dto) { String key = "Replylock" + dto.getBookNo() + "_" + dto.getUserId(); // 获取分布式锁 RLock lock=redissonClient.getLock(key); // 尝试获取分布式锁,如果返回true,即代表成功获取了分布式锁 try { log.info("开始抢购"); boolean result = lock.tryLock(100,5L,TimeUnit.SECONDS); if (result) { // 判断此书籍是否能够被抢购 BookStock bookStock = bookStockMapper.selectBookRobByBookNo(dto.getBookNo()); if (bookStock == null || Integer.valueOf(bookStock.getStock()) <= 0) { return ResultVo.error("库存不足"); } // 先查询用户是否抢购过此书籍 Integer buyedTotal = bookRobMapper.countByBookNoUserId(dto.getUserId(), dto.getBookNo()); if (buyedTotal > 0) { return ResultVo.error("您已抢购过该书籍"); } bookStock.setStock(String.valueOf(Integer.valueOf(bookStock.getStock()) - 1)); log.info("更新之前的库存" + bookStock.getStock()); int res = bookStockMapper.updateStockWithLock(dto.getBookNo()); log.info("更新库存完成"); if (res > 0 ) { // 创建书籍抢购记录实体信息 BookRob entity = new BookRob(); // 从提交的用户抢购请求实体信息中对应的字段取值 // 复制到新创建的书籍抢购记录实体的相应字段中 BeanUtils.copyProperties(dto,entity); // 设置抢购时间 entity.setRobTime(new Date()); // 插入用户注册信息 bookRobMapper.insertSelective(entity); log.info("插入抢购记录成功"); } return ResultVo.success("抢购成功"); }else { log.error("库存不足1"); } } catch (InterruptedException e) { return ResultVo.error("异常"); } finally { // 释放锁 lock.forceUnlock(); } return ResultVo.error("抢购失败"); } <update id="updateStockWithLock"> update book_stock SET stock = stock - 1 where isActive=1 AND book_no=#{bookNo} and stock > 0 and (stock -1)>=0 </update>