一、什么是分布式锁?


线程锁:主要用来给方法、代码块加锁。当某个方法或代码使用锁,在同一时刻仅有一个线程执行该方法或该代码段。线程锁只在同一JVM中有效果,因为线程锁的实现在根本上是依靠线程之间共享内存实现的,比如synchronized是共享对象头,显示锁Lock是共享某个变量(state)

进程锁:为了控制同一操作系统中多个进程访问某个共享资源,因为进程具有独立性,各个进程无法访问其他进程的资源,因此无法通过synchronized等线程锁实现进程锁。

分布式锁:当多个进程不在同一个系统中,用分布式锁控制多个进程对资源的访问。

二、分布式锁的特性?

  • 互斥性: 任意时刻,只有一个客户端能持有锁。
  • 锁超时释放:持有锁超时,可以释放,防止不必要的资源浪费,也可以防止死锁。
  • 可重入性:一个线程如果获取了锁之后,可以再次对其请求加锁。
  • 高性能和高可用:加锁和解锁需要开销尽可能低,同时也要保证高可用,避免分布式锁失效。
  • 安全性:锁只能被持有的客户端删除,不能被其他客户端删除

三、基于redis实现分布式锁

1.Redis分布式锁方案一:SETNX + EXPIRE

即先用setnx来抢锁,如果抢到之后,再用expire给锁设置一个过期时间,防止锁忘记释放

// 获取锁 基于 setnx 和 expire 此方法不会保证原子性 可以使用lua脚本(redis 又演变出 set加过期时间的方式)public boolean getLockNx(Jedis jedis, String lockeKey, String requestId, Long expireTime) {Long setnx = jedis.setnx(lockeKey, requestId);if (Objects.equals(setnx, 1)) {jedis.expire(lockeKey, new Long(expireTime).intValue());return true;}return false;}

2.Redis分布式锁方案二:SET的扩展命令(SET EX PX NX)

  // 获取锁, 设置超时时间,单位为毫秒 此方法目前可以满足大多数需求public boolean getLock(Jedis jedis, String lockKey, String requestId, Long expireTime) {String result = jedis.set(lockKey, requestId, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);if (LOCK_SUCCESS.equals(result)) {return true;}return false;}

存在问题:

  1. 问题一:「锁过期释放了,业务还没执行完」。假设线程a获取锁成功,一直在执行临界区的代码。但是100s过去后,它还没执行完。但是,这时候锁已经过期了,此时线程b又请求过来。显然线程b就可以获得锁成功,也开始执行临界区的代码。那么问题就来了,临界区的业务代码都不是严格串行执行的啦。
  2. 问题二:「锁被别的线程误删」。假设线程a执行完后,去释放锁。但是它不知道当前的锁可能是线程b持有的(线程a去释放锁时,有可能过期时间已经到了,此时线程b进来占有了锁)。那线程a就把线程b的锁释放掉了,但是线程b临界区业务代码可能都还没执行完呢

3.Redis分布式锁方案三:分布式锁的释放

//释放锁 存在问题是会误删他人的锁public boolean releaseLock(Jedis jedis1, String key, String requestId) {try {String result = jedis1.get(key);if (Objects.equals(result, requestId)) {// lockkey锁失效,下一步删除的就是别人的锁jedis1.del(key);return true;}} catch (Exception ex) {ex.printStackTrace();} finally {jedis1.close();}return false;}
   /*** 释放分布式锁 基于lua脚本释放 保证了原子性 和释放锁是符合自己的 解决并发问题** @param jedis     Redis客户端* @param lockKey   锁* @param requestId 请求标识* @return 是否释放成功*/public static boolean releaseDistributedLock(Jedis jedis, String lockKey, String requestId) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));if (LOCK_SUCCESS.equals(result)) {return true;}return false;}

4.Redis分布式锁方案四:Redisson框架

方案3还是可能存在「锁过期释放,业务没执行完」的问题。有些h会认为,稍微把锁过期时间设置长一些就可以啦。其实我们设想一下,是否可以给获得锁的线程,开启一个定时守护线程,每隔一段时间检查锁是否还存在,存在则对锁的过期时间延长,防止锁过期提前释放

开源框架Redisson解决了这个问题。我们一起来看下Redisson底层原理图吧:

只要线程一加锁成功,就会启动一个watch dog看门狗,它是一个后台线程,会每隔10秒检查一下,如果线程1还持有锁,那么就会不断的延长锁key的生存时间。因此,Redisson就是使用Redisson解决了「锁过期释放,业务没执行完」问题

 //redlock 实现分布式锁 最终方案public boolean getRedLock(RedissonClient redisClient){RLock lock = redisClient.getLock("REDLOCK_KEY");try {boolean flag = lock.tryLock();if (flag) {System.out.println("加锁成功");}} catch (Exception ex){} finally {lock.unlock();}return false;}

redis分布式锁解决方案相关推荐

  1. 面试官:Redis分布式锁解决方案是什么?

    今天博主在这片文章中主要给大家讲下Redis分布式锁的原理以及解决方案 学到三连呦 1.Redis分布式锁原理 1.1.简述 我们知道分布式锁的特性是排他.避免死锁.高可用.分布式锁的实现可以通过数据 ...

  2. 深入理解分布式技术 - Redis 分布式锁解决方案

    文章目录 Pre 分布式锁特征 使用 setnx 实现分布式锁 使用 setnx 和 expire 实现 使用 set 扩展命令实现 分布式锁的高可用 集群下分布式锁存在哪些问题 Redlock 算法 ...

  3. Redis 作者 Antirez 讲如何实现分布式锁?Redis 实现分布式锁天然的缺陷分析Redis分布式锁的正确使用姿势!...

    Redis分布式锁基本原理 采用 redis 实现分布式锁,主要是利用其单线程命令执行的特性,一般是 setnx, 只会有一个线程会执行成功,也就是只有一个线程能成功获取锁:看着很完美. 然而-- 看 ...

  4. 分布式锁-Redis红锁解决方案

    文章目录 1:分布式锁的概念 1:概念 2:锁/分布式锁/事务区别 2:本文使用的案例场景 1:需求 2:controller层代码 3:锁控制层代码(使用synchronized 不成功) 4:调用 ...

  5. 快来学习Redis 分布式锁的背后原理

    以前在学校做小项目的时候,用到Redis,基本也只是用来当作缓存.可阿粉在工作中发现,Redis在生产中并不只是当作缓存这么简单.在阿粉接触到的项目中,Redis起到了一个分布式锁的作用,具体情况是这 ...

  6. Redis分布式锁使用不当,酿成一个重大事故,超卖了100瓶飞天茅台!!!

    点击关注公众号,Java干货及时送达 来源:juejin.cn/post/6854573212831842311 基于Redis使用分布式锁在当今已经不是什么新鲜事了. 本篇文章主要是基于我们实际项目 ...

  7. Redis 分布式锁使用不当,酿成一个重大事故,超卖了100瓶飞天茅台!!!

    点击上方蓝色"方志朋",选择"设为星标" 回复"666"获取独家整理的学习资料! 基于Redis使用分布式锁在当今已经不是什么新鲜事了. 本 ...

  8. 秒杀商品超卖事故:Redis分布式锁请慎用!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:浪漫先生 来源:juejin.im/post/6854573 ...

  9. 记一次由Redis分布式锁造成的重大事故,避免以后踩坑!

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 作者:浪漫先生 juejin.im/post/5f159cd8f2 ...

最新文章

  1. DotNetNuke(DNN)升级攻略(DNN 4.3.7至DNN 4.6.0)
  2. 手游引擎之战再现新挑战者,OGEngine来了
  3. java ftp connect_java操作Ftp文件的一些方式(一)
  4. 句句真研—每日长难句打卡Day10
  5. git原理和常用操作
  6. STL---栈和队列
  7. php操作mysql数据_详解PHP操作MySQL数据库
  8. 【前端词典】进阶必备的网络基础
  9. 六维空间向量表示法公式笔记
  10. 国庆专属头像、国旗专属头像一键生成源代码
  11. Python绘制bezier曲线
  12. 计算机用户接入最快的,行测真题_2013-2017年固定互联网宽带接入用户数的年增长速度最快的年份是...
  13. 计算机科学与技术要求具备的能力,计算机科学与技术专业毕业要求
  14. vue老项目升级vue-cli3.0问题总结
  15. docker rabbitmq error: touch cannot touch ‘/etc/rabbitmq/rabbitmq.conf‘ permission denied
  16. 信息学奥赛一本通1258:数字金字塔
  17. 雷达原理第五版微盘pdf下载_雷达原理(第5版电子信息类精品教材)
  18. 第一个 Python 程序(2)
  19. 复杂科学在创客教学研究中的应用
  20. A morphable model for the synthesis of 3D faces 学习笔记(未完)

热门文章

  1. 图观——渲染一个简易的三维场景
  2. 【Maven】maven下载网址进不去处理方法
  3. 平安银行广州分行:以金融赋能慈善 释放更大社会效能
  4. 关于感染型病毒ramnit和runner的查杀记事
  5. 每天学一点英文:Espresso 20210811
  6. JavaScript 中的事件类型5(读书笔记思维导图)
  7. finclip设计指南与小程序设计指南
  8. .NET新手系列(九)
  9. LINUX定时清理文件定时任务
  10. 简易 文章发布系统——前台界面