实现方式一:存在抛异常后lock值无法归0的问题

@Autowired
private StringRedisTemplate stringRedisTemplate;@RequestMapping("deduct_stock")public void deductStock(){Long num = stringRedisTemplate.opsForValue().increment("lock", 1);//多个线程过来  只有一个线程会将num值设置为1  其余的线程都不可能为1if (num==1){int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));if (stock>0){stringRedisTemplate.opsForValue().set("stock",(stock-1)+"");System.out.println("扣减成功,库存stock:"+(stock-1)+"");}else{System.out.println("扣减失败,库存不足");}}//还原stringRedisTemplate.opsForValue().increment("lock",-1);}

实现方式二:加try…finally。无论程序是否抛出异常,最终都会还原。但是在还原为0的时候有可能redis挂了,或者是程序还没执行完try代码块中的内容,整个web应用挂掉了,finally块中的内容也无法执行。即使程序重启,后面的线程也始终无法满足num==1的条件。

@Autowired
private StringRedisTemplate stringRedisTemplate;@RequestMapping("deduct_stock")
public void deductStock(){try{Long num = stringRedisTemplate.opsForValue().increment("lock", 1);//多个线程过来  只有一个线程会将num值设置为1  其余的线程都不可能为1if (num==1){int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));if (stock>0){stringRedisTemplate.opsForValue().set("stock",(stock-1)+"");System.out.println("扣减成功,库存stock:"+(stock-1)+"");}else{System.out.println("扣减失败,库存不足");}}}finally {//还原stringRedisTemplate.opsForValue().increment("lock",-1);}
}

实现方式三:设置lock的超时时间。存在这么一个场景,由于某一段时间内,网络环境差,导致本应10秒内就执行完的程序执行了15秒才完成,而锁已经过期了,也就意味着后面的线程能拿到锁,导致锁形同虚设。
还存在这么一个情况:第一个线程执行时长15秒,假设第二个线程执行时长8秒,由于锁已失效,第二个线程是能重新拿到新锁的,结果第一个线程在执行归0操作释放锁时,它自己的锁已失效,导致释放的并不是自己的锁,而是第二个线程的锁。总之,存在一系列场景导致锁失效。

@Autowired
private StringRedisTemplate stringRedisTemplate;@RequestMapping("deduct_stock")
public void deductStock(){try{String lockkey = "lock";Long num = stringRedisTemplate.opsForValue().increment(lockkey, 1);//给key设置超时时间 到期自动清理stringRedisTemplate.expire(lockkey,10, TimeUnit.SECONDS);//多个线程过来  只有一个线程会将num值设置为1  其余的线程都不可能为1if (num==1){int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));if (stock>0){stringRedisTemplate.opsForValue().set("stock",(stock-1)+"");System.out.println("扣减成功,库存stock:"+(stock-1)+"");}else{System.out.println("扣减失败,库存不足");}}}finally {//还原stringRedisTemplate.opsForValue().increment("lock",-1);}
}

这种场景下,可以开启一个分线程,与当前线程绑定,如果锁的失效时间是10秒,那么就每隔5秒去扫描一下这把锁,看看锁是否还在,如果还在就再次将失效时间重置为10秒,不断延时,也就相当于让这把锁具备了自动延时的功能,直到当前线程亲自将这把锁释放,否则一致延时下去。

Redisson框架实现分布式锁

上述分析也就是Redisson的实现原理,只不过会增加一个线程,让等待的线程不断地尝试加锁,通过while循环来实现的,俗称就是自旋加锁。

配置单机Redis

/*** @ProjectName springbootdemo_src* @ClassName RedissionConfig* @Desicription TODO* @Author Zhang Xueliang* @Date 2019/7/27 17:34* @Version 1.0**/
@Configuration
public class RedissonConfig {@Bean("redisson")//如果不写value  默认就会以方法名作为bean的名称进行注入public Redisson getRedisson(){Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379").setDatabase(0);return (Redisson) Redisson.create(config);}
}

加锁代码:很简单,就三行代码。重要的是理解运行原理。

/*** @ProjectName springbootdemo_src* @ClassName RedissionController* @Desicription TODO* @Author Zhang Xueliang* @Date 2019/7/27 15:55* @Version 1.0**/@SuppressWarnings("all")
@RestController
public class RedissionController {@Autowiredprivate Redisson redisson;@RequestMapping("redisson_lock")public void redissonDeductStock() {String lockkey = "lock";RLock lock = redisson.getLock(lockkey);try {lock.lock();//如果使用默认的午餐lock方法 默认超时时间设置的是30秒 可以传入自定义的超时时间int stock = Integer.parseInt(stringRedisTemplate.opsForValue().get("stock"));if (stock > 0) {stringRedisTemplate.opsForValue().set("stock", (stock - 1) + "");System.out.println("扣减成功,库存stock:" + (stock - 1) + "");} else {System.out.println("扣减失败,库存不足");}} finally {lock.unlock();}}}

Redisson实质上就是给代码加上了一把悲观锁,而悲观锁是比较影响性能的。increment自增加1的方式实质是乐观锁。
Redis天生就是单线程的,在高并发环境主从集群还是会存在一定问题。

Redisson分布式锁实战(适用于Redis高并发场景)相关推荐

  1. redisson分布式锁,实战

    目录 什么时候用分布式锁? 分布式锁入门 超时设置 释放了不是自己加的锁 正确设置锁超时 加解锁代码位置有讲究 实现可重入锁 Redis Hash 可重入锁 主从架构带来的问题 什么是 Redlock ...

  2. Redisson分布式锁实战-2:解决wait_time之坑

    我们一起来分析一下原因,我们获取锁之后,我们只打印了一个日志,然后从配置文件里面拿到一个hour,然后就结束了,结束之后就来到finally里边,而这个时间并没有执行SQL语句,所以他的时间会非常非常 ...

  3. Redisson分布式锁实战-1:构建分布式锁

    我们现在来到Task类当中,这个方法就是V4了/*** Redisson分布式锁实现* @throws InterruptedException*/ // @Scheduled(cron=" ...

  4. Redis高并发场景下秒杀超卖解决

    目录 1 什么是秒杀 2 为什么要防止超卖 3 单体架构常规秒杀 3.1 常规减库存代码 3.2 模拟高并发 3.3 超卖现象 3.4 分析原因 4 简单实现悲观乐观锁解决单体架构超卖 4.1 悲观锁 ...

  5. 用分布式锁来防止库存超卖,但是是每秒上千订单的高并发场景,如何对分布式锁进行高并发优化来应对这个场景?

    用分布式锁来防止库存超卖,但是是每秒上千订单的高并发场景,如何对分布式锁进行高并发优化来应对这个场景? 转载 codeing_doc 最后发布于2018-11-23 09:44:41 阅读数 1073 ...

  6. Redisson分布式锁的配置和使用

    基于springBoot的redisson分布式锁 之前使用Redis分布式锁都是自己写的工具类,利用Redis的setNX特性:后来发现Redisson提供的分布式锁是真的好用. Redisson可 ...

  7. RedisSon高并发分布式锁实战RedisSon源码解读

    Redis高并发分布式锁实战 1.分布式场景下的synchronized失效的问题–用redis实现分布式锁 synchronized是通过monitor实现的jvm级别的锁,如果是分布式系统,跑在不 ...

  8. Redis多容器高并发场景 , 设置缓存的时候,要考虑多容器加锁的场景。(incr计数和redis分布式锁区别)

    1.设置缓存的时候,要考虑多容器加锁的场景. (1)场景,短信回执场景,会有二次回执的情况,但是我们只处理一次回执的消息体,如何不处理二次回执呢? // 队列中有数据且容量未达到100,可继续放入队列 ...

  9. Redis实战——Redisson分布式锁

    目录 1 基于Redis中setnx方法的分布式锁的问题 2 Redisson 2.1 什么是Redisson 2.2 Redisson实现分布式锁快速入门 2.3 Redisson 可重入锁原理 什 ...

最新文章

  1. 现身说法:37岁老码农找工作
  2. sklearn随机森林概述
  3. 2022春招马蜂窝旅游网第一轮面试 面经
  4. php反射机制与依赖注入,利用反射机制实现基本的依赖注入
  5. mysql创建数据库sql语句
  6. 计算机多媒体制作三级证书,媒体报道:计算机职业资格证书有哪些
  7. Struts 2的基石——拦截器(Interceptor)详细讲解
  8. 宽带拨号上服务器无响应,宽带拨号服务器无响应(图文)
  9. 目标检测 | 丰富特征导向Refinement Network用于目标检测(附github源码)
  10. pytest assert 封装
  11. 引用 和指针 ,简单, 一怔见血
  12. 计算机教学音乐,计算机音乐的教学和应用研究
  13. 办公系统租用云主机建站用云主机
  14. picturebox绑定datagridview
  15. 快手大数据平台服务化实践
  16. 基于Python实现微信公众号爬虫进行数据分析
  17. 中国的超级计算机叫什么名,超级计算机中国排名是什么?
  18. 单线、双线、三线、BGP服务器在使用上的区别?
  19. 麦咖啡携手洗护品牌多芬发布“泡泡拿铁”;三星发布2亿像素传感器 | 美通社头条...
  20. matlab 流固耦合,一种基于MATLAB-ANSYS软件的静压溜板流固耦合工作性能的计算方法与流程...

热门文章

  1. 【机器学习基础】常见二分类损失函数、距离度量的Python实现
  2. 卷积神经网络之 - Lenet
  3. 那些年做的学术公益-你不是一个人在战斗
  4. 玩转可视化--来聊聊地图投影的学问
  5. nginx: [emerg] getpwnam(nginx) failed in /usr/local/nginx/conf/nginx.conf:2
  6. javascript   卸载事件(onunload)
  7. CentOS 7 Root用户密码重置 2017-04-02
  8. Git使用技巧(3)-- 远程操作
  9. 原型图Mockplus:怎样使用图片裁切功能
  10. [转]C#多线程学习(三) 生产者和消费者