redis分布式锁的使用
Posted shouyaya
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis分布式锁的使用相关的知识,希望对你有一定的参考价值。
1.通过官方文档查看相关的命令操作:http://www.redis.cn/commands.html
关键命令:setIfAbsent(String key,String value)对应官方文档的 setnx
解释:将key设置值为value,如果key不存在,这种情况下等同SET命令。 当key存在时,什么也不做。SETNX是”SET if Not eXists”的简写。
getAndSet
解释:自动将key对应到value并且返回原来key对应的value。如果key存在但是对应的value不是字符串,就返回错误。(get旧值,set新值)
2.编写redisLock,即上锁和解锁的方法:
@Service @Slf4j public class RedisLock { @Autowired StringRedisTemplate template; public boolean lock(String key,String value){ //1.如果能设置value表示锁没被占用,返回true表示上锁成功,否则进入下一步 if (template.opsForValue().setIfAbsent(key,value)) { return true; } String currentValue = template.opsForValue().get(key); //2.这一步是为了防止死锁,因为如果被上锁的那段程序抛出异常,会导致无法执行解锁方法从而导致死锁 //所以value的值应设为当前时间+超时时间,当Redis里的currentValue小于线程当前的系统时间, //说明死锁产生了,需解开这个死锁 if(!StringUtils.isEmpty(currentValue)&&Long.valueOf(currentValue)<System.currentTimeMillis()) { //3.解死锁的操作,把原来的currentValue设置为新的value,并为拿到锁的新线程重新上锁 String oldValue = template.opsForValue().getAndSet(key, value); //4.这步是为了防止在死锁产生的时候,同时有多个线程进来上锁 // ,只有oldValue等于currentValue的线程才可成功上锁 if(!StringUtils.isEmpty(oldValue)&&oldValue.equals(currentValue)) { template.opsForValue().set(key,value); return true; } } //上锁失败 return false; } public void unlock(String key,String value){ try { String currentValue = template.opsForValue().get(key); //只有在Redis里面的CurrentValue等于传进来的Value才能解锁 if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) { template.delete(key); } }catch (Exception e){ log.error("【redis分布式锁】解锁异常, {}", e); } } }
3.在相应的程序里调用redisLock
@Override public String orderSecKillProduct(String productId) { //value的值设为当前时间+超时时间 String time=String.valueOf(System.currentTimeMillis()+10*1000); //加锁 if(!redisLock.lock(productId,time)){ //上锁失败,抛出异常跳转到相应的页码 throw new SellException(101,"太多人抢购了,请重试"); } //查询库存若为0则,提示已抢购完 Integer stockNum = productStock.get(productId); if(stockNum==0){ throw new SellException(100,"已被抢购完了"); } //下单 ordered.put(KeyUtil.genUniqueKey(), productId); //减库存 stockNum=stockNum-1; try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } //存入数据库 productStock.put(productId,stockNum); //解锁 redisLock.unlock(productId,time); //查询 return query(productId); }
setIfAbsent
以上是关于redis分布式锁的使用的主要内容,如果未能解决你的问题,请参考以下文章
Redis进阶学习03---Redis完成秒杀和Redis分布式锁的应用
80% 人不知道的 Redis 分布式锁的正确实现方式(Java 版)