文章目录

  • 1. 单机版没有加锁
  • 2. 单机版加锁
  • 3. 引入 Redis 分布式锁
  • 4. 加锁 解锁,lock/unlock 必须同时出现并保证调用
  • 5. 加入锁过期时间
  • 6. 加锁且携带锁过期时间 原子性
  • 7. 删除自己的锁
  • 8.1 Redis 自身事务
  • 8.2 Lua 脚本方式
  • 9.1 引入 Redisson
  • 9.2 Redisson 超高并发解决方案 (推荐)

1. 单机版没有加锁

/*** @author Mr.superbeyone**/
public class RedisLockTest {@AutowiredStringRedisTemplate stringRedisTemplate;private final Lock lock = new ReentrantLock();/*** 单机版 没有加锁,出现超卖现象*/public void test1() {String key = "key";String countVal = stringRedisTemplate.opsForValue().get(key);int count = countVal == null ? 0 : Integer.parseInt(countVal);if (count > 0) {int num = count - 1;stringRedisTemplate.opsForValue().set(key, String.valueOf(num));}}}

问题:出现超卖现象

2. 单机版加锁

/*** @author Mr.superbeyone**/
public class RedisLockTest {@AutowiredStringRedisTemplate stringRedisTemplate;private final Lock lock = new ReentrantLock();/*** synchronized 不见不散*/public void test2() {synchronized (this) {String key = "key";String countVal = stringRedisTemplate.opsForValue().get(key);int count = countVal == null ? 0 : Integer.parseInt(countVal);if (count > 0) {int num = count - 1;stringRedisTemplate.opsForValue().set(key, String.valueOf(num));}}}/*** lock 过期不候*/public void test3() throws InterruptedException {String key = "key";if (lock.tryLock(20, TimeUnit.MILLISECONDS)) {try {String countVal = stringRedisTemplate.opsForValue().get(key);int count = countVal == null ? 0 : Integer.parseInt(countVal);if (count > 0) {int num = count - 1;stringRedisTemplate.opsForValue().set(key, String.valueOf(num));}} finally {lock.unlock();}} else {//没有拿到锁的业务逻辑}}
}

问题:分布式部署后,单机锁还是会出现超卖现象,需要分布式锁

3. 引入 Redis 分布式锁

public class RedisLockTest {@AutowiredStringRedisTemplate stringRedisTemplate;private static final String REDIS_LOCK = "redis_lock";/*** 引入 Redis 分布式锁*/public void test4() {String value = UUID.randomUUID().toString() + Thread.currentThread().getName();//加锁Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK, value);//相当于 setNXif (!flag) {//获取锁失败return;}String key = "key";String countVal = stringRedisTemplate.opsForValue().get(key);int count = countVal == null ? 0 : Integer.parseInt(countVal);if (count > 0) {int num = count - 1;stringRedisTemplate.opsForValue().set(key, String.valueOf(num));//解锁stringRedisTemplate.delete(REDIS_LOCK);}}
}

问题 :有可能不能释放锁

4. 加锁 解锁,lock/unlock 必须同时出现并保证调用

public class RedisLockTest {@AutowiredStringRedisTemplate stringRedisTemplate;private static final String REDIS_LOCK = "redis_lock";/*** 引入 Redis 分布式锁* 加锁 解锁,lock/unlock 必须同时出现并保证调用*/public void test5() {String value = UUID.randomUUID().toString() + Thread.currentThread().getName();try {//加锁Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK, value);//相当于 setNXif (!flag) {//获取锁失败return;}String key = "key";String countVal = stringRedisTemplate.opsForValue().get(key);int count = countVal == null ? 0 : Integer.parseInt(countVal);if (count > 0) {int num = count - 1;stringRedisTemplate.opsForValue().set(key, String.valueOf(num));}} finally {//解锁stringRedisTemplate.delete(REDIS_LOCK);}}
}

问题:机器宕机,不能保证最后的解锁

5. 加入锁过期时间

public class RedisLockTest {@AutowiredStringRedisTemplate stringRedisTemplate;private static final String REDIS_LOCK = "redis_lock";/*** 引入 Redis 分布式锁* 加锁 解锁,lock/unlock 必须同时出现并保证调用* 锁 自动删除*/public void test6() {String value = UUID.randomUUID().toString() + Thread.currentThread().getName();try {//加锁Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK, value);//相当于 setNXstringRedisTemplate.expire(REDIS_LOCK, 10L, TimeUnit.SECONDS);if (!flag) {//获取锁失败return;}String key = "key";String countVal = stringRedisTemplate.opsForValue().get(key);int count = countVal == null ? 0 : Integer.parseInt(countVal);if (count > 0) {int num = count - 1;stringRedisTemplate.opsForValue().set(key, String.valueOf(num));}} finally {//解锁stringRedisTemplate.delete(REDIS_LOCK);}}
}

问题:加锁和设置过期时间 没有保证原子性

6. 加锁且携带锁过期时间 原子性

public class RedisLockTest {@AutowiredStringRedisTemplate stringRedisTemplate;private static final String REDIS_LOCK = "redis_lock";/*** 引入 Redis 分布式锁* 加锁 解锁,lock/unlock 必须同时出现并保证调用* 锁 自动删除 原子性操作*/public void test7() {String value = UUID.randomUUID().toString() + Thread.currentThread().getName();try {//加锁Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK, value, 10L, TimeUnit.SECONDS);//相当于 setNXif (!flag) {//获取锁失败return;}String key = "key";String countVal = stringRedisTemplate.opsForValue().get(key);int count = countVal == null ? 0 : Integer.parseInt(countVal);if (count > 0) {int num = count - 1;stringRedisTemplate.opsForValue().set(key, String.valueOf(num));}} finally {//解锁stringRedisTemplate.delete(REDIS_LOCK);}}
}

问题:处理业务的时间大于设置的锁过期时间,线程B可能删除线程A的锁,删除别人的锁

7. 删除自己的锁

public class RedisLockTest {@AutowiredStringRedisTemplate stringRedisTemplate;private static final String REDIS_LOCK = "redis_lock";/*** 引入 Redis 分布式锁* 加锁 解锁,lock/unlock 必须同时出现并保证调用* 锁 自动删除 原子性操作* 删除自己的锁*/public void test8() {String value = UUID.randomUUID().toString() + Thread.currentThread().getName();try {//加锁Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK, value, 10L, TimeUnit.SECONDS);//相当于 setNXif (!flag) {//获取锁失败return;}String key = "key";String countVal = stringRedisTemplate.opsForValue().get(key);int count = countVal == null ? 0 : Integer.parseInt(countVal);if (count > 0) {int num = count - 1;stringRedisTemplate.opsForValue().set(key, String.valueOf(num));}} finally {//解锁//删除自己的锁if(stringRedisTemplate.opsForValue().get(REDIS_LOCK).equals(value)) {stringRedisTemplate.delete(REDIS_LOCK);}}}
}

问题:删除自己的锁判断 与 删除操作 不是原子性 ,可能判断加锁与解锁的不是同一个客户端,有可能会出现误解锁
可以使用 Lua 脚本 解决

if redis.call("get",KEYS[1]) == ARGV[1]
thenreturn redis.call("del",KEYS[1])
elsereturn 0
end

8.1 Redis 自身事务

public class RedisLockTest {@AutowiredStringRedisTemplate stringRedisTemplate;private static final String REDIS_LOCK = "redis_lock";/*** 引入 Redis 分布式锁* 加锁 解锁,lock/unlock 必须同时出现并保证调用* 锁 自动删除 原子性操作* 删除自己的锁* 删除自己的锁判断 与 删除操作 原子性* Redis 自身事务*/public void test9() {String value = UUID.randomUUID().toString() + Thread.currentThread().getName();try {//加锁Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK, value, 10L, TimeUnit.SECONDS);//相当于 setNXif (!flag) {//获取锁失败return;}String key = "key";String countVal = stringRedisTemplate.opsForValue().get(key);int count = countVal == null ? 0 : Integer.parseInt(countVal);if (count > 0) {int num = count - 1;stringRedisTemplate.opsForValue().set(key, String.valueOf(num));}} finally {//解锁//删除自己的锁//加入 Redis 事务while (true) {stringRedisTemplate.watch(REDIS_LOCK);if (stringRedisTemplate.opsForValue().get(REDIS_LOCK).equals(value)) {stringRedisTemplate.setEnableTransactionSupport(true);stringRedisTemplate.multi();stringRedisTemplate.delete(REDIS_LOCK);List<Object> list = stringRedisTemplate.exec();if (list == null) {continue;}}stringRedisTemplate.unwatch();//删除锁成功break;}}}
}

问题:确保 Redis 过期时间大于业务执行时间的问题,Redis 分布式锁如何续期?

8.2 Lua 脚本方式

public class RedisLockTest {@AutowiredStringRedisTemplate stringRedisTemplate;private static final String REDIS_LOCK = "redis_lock";/*** 引入 Redis 分布式锁* 加锁 解锁,lock/unlock 必须同时出现并保证调用* 锁 自动删除 原子性操作* 删除自己的锁* 删除自己的锁判断 与 删除操作 原子性* Lua 脚本*/public void test10() {String value = UUID.randomUUID().toString() + Thread.currentThread().getName();try {//加锁Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(REDIS_LOCK, value, 10L, TimeUnit.SECONDS);//相当于 setNXif (!flag) {//获取锁失败return;}String key = "key";String countVal = stringRedisTemplate.opsForValue().get(key);int count = countVal == null ? 0 : Integer.parseInt(countVal);if (count > 0) {int num = count - 1;stringRedisTemplate.opsForValue().set(key, String.valueOf(num));}} finally {//解锁//删除自己的锁Jedis jedis = null; // 从连接池中 获取 jedisString script = "if redis.call('get',KEYS[1]) == ARGV[1]" +"then" +"    return redis.call('del',KEYS[1])" +"else" +"    return 0" +"end";try {Object eval = jedis.eval(script, Collections.singletonList(REDIS_LOCK), Collections.singletonList(value));if ("1".equals(eval.toString())) {//删除锁成功} else {//删除锁失败}} finally {if (null != jedis) {jedis.close();}}}}
}

问题:确保 Redis 过期时间大于业务执行时间的问题,Redis 分布式锁如何续期?

9.1 引入 Redisson

<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.14.1</version>
</dependency>
public class RedisConfig {@Beanpublic Redisson redisson() {Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379").setDatabase(0);return (Redisson) Redisson.create(config);}
}

public class RedisLockTest {@AutowiredStringRedisTemplate stringRedisTemplate;private static final String REDIS_LOCK = "redis_lock";@AutowiredRedisson redisson;/*** 引入 Redis 分布式锁* 加锁 解锁,lock/unlock 必须同时出现并保证调用* 锁 自动删除 原子性操作* 删除自己的锁* Redisson*/public void test11() {//加锁RLock redissonLock = redisson.getLock(REDIS_LOCK);try {redissonLock.lock();String key = "key";String countVal = stringRedisTemplate.opsForValue().get(key);int count = countVal == null ? 0 : Integer.parseInt(countVal);if (count > 0) {int num = count - 1;stringRedisTemplate.opsForValue().set(key, String.valueOf(num));}} finally {//解锁redissonLock.unlock();}}}

问题:超高并发情况下,可能出现 IllegalMonitorStateException: attempt to unlock, no locked by current thread by node id …

9.2 Redisson 超高并发解决方案 (推荐)

/*** 引入 Redis 分布式锁* 加锁 解锁,lock/unlock 必须同时出现并保证调用* 锁 自动删除 原子性操作* 删除自己的锁* Redisson*/public void test12() {//加锁RLock redissonLock = redisson.getLock(REDIS_LOCK);try {redissonLock.lock();String key = "key";String countVal = stringRedisTemplate.opsForValue().get(key);int count = countVal == null ? 0 : Integer.parseInt(countVal);if (count > 0) {int num = count - 1;stringRedisTemplate.opsForValue().set(key, String.valueOf(num));}} finally {//解锁if (redissonLock.isLocked() && redissonLock.isHeldByCurrentThread()) {redissonLock.unlock();       }}}

Mr.superbeyone


十步学习 Redis 分布式锁相关推荐

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

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

  2. 还不知道 Redis 分布式锁的背后原理?还不赶快学习一下

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

  3. 关于分布式锁原理的一些学习与思考:redis分布式锁,zookeeper分布式锁

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:牛人 20000 字的 Spring Cloud 总结,太硬核了~ 作者:队长给我球. 出处:https://w ...

  4. zookeeper 分布式锁_关于redis分布式锁,zookeeper分布式锁原理的一些学习与思考

    编辑:业余草来源:https://www.xttblog.com/?p=4946 首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法 ...

  5. redis cluster 分布式锁_关于分布式锁原理的一些学习与思考redis分布式锁,zookeeper分布式锁...

    首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方法.变量. 在一个进程中,也就是一个jvm 或者说应用中,我们很容易去处理控制,在j ...

  6. 关于redis分布式锁,zookeeper分布式锁原理的一些学习与思考

    编辑:业余草 来源:https://www.xttblog.com/?p=4946 首先分布式锁和我们平常讲到的锁原理基本一样,目的就是确保,在多个线程并发时,只有一个线程在同一刻操作这个业务或者说方 ...

  7. 【使用Redis分布式锁实现优惠券秒杀功能】-Redis学习笔记05

    前言 本章节主要实现限时.限量优惠券秒杀功能,并利用分布式锁解决<超卖问题>.<一人一单问题>. 一.优惠券下单基本功能实现 1.功能介绍及流程图 2.代码实现 @Resour ...

  8. getset原子性 redis_一文看透 Redis 分布式锁进化史(解读 + 缺陷分析)

    各个版本的Redis分布式锁 V1.0 V1.1 基于[GETSET] V2.0 基于[SETNX] V3.0 V3.1 分布式Redis锁:Redlock 总结 <Netty 实现原理与源码解 ...

  9. 一个项目部署多个节点会导致锁失效么_一文看透 Redis 分布式锁进化史(解读 + 缺陷分析)...

    各个版本的Redis分布式锁 V1.0 V1.1 基于[GETSET] V2.0 基于[SETNX] V3.0 V3.1 分布式Redis锁:Redlock 总结 <Netty 实现原理与源码解 ...

最新文章

  1. 关于numpy中eye和identity的区别详解
  2. 20155328 《信息安全系统设计基础》 课程总结
  3. SilverLight4:在MVVM架构下实现模式窗口
  4. react(83)--filter
  5. mysql数据库序列作用_MySQL 序列使用
  6. 类间关系有很多种 UML
  7. C语言中如何将小数或整数和字符串合二为一
  8. 大数据最核心的关键技术:32个算法
  9. 五千的手机和两三千的手机使用起来有什么不一样?有必要买贵的吗?
  10. 上周末Jscex项目介绍的幻灯片
  11. Git学习笔记--廖雪峰官网教程
  12. freeswitch安装
  13. 三星s8 android9.0官方rom,三星S8+港版安卓9官方固件rom刷机包:TGY-G9550ZHU3DSD3
  14. 【ZYNQ】IP核_DDR4_SDRAM(MIG)的详细介绍
  15. ORAN C平面 Section Type 0
  16. 国内技术管理人员批阅google的“春运交通图”项目
  17. 问卷调查 批量模拟真人填写 爬虫 实战
  18. [洛谷 P4084 USACO17DEC] Barn Painting G (树形dp经典)
  19. java设计九宫格拼图软件哪个好用_九宫格拼图软件下载_抖音很火的九宫格拼图软件app下载_易玩网...
  20. 华大半导体HC32F4A0笔记(一),PWM输入捕获,使用TIM6

热门文章

  1. TensorFlow训练参数存为npy格式并调用——线性回归
  2. 帮我写一个拦截APP广告的程序
  3. 新标准日本语中级下(第6单元)笔记
  4. 【python】matplotlib画小猪
  5. Glide源码解析之山清水秀疑无路(一)
  6. Halcon学习:calibrate_hand_eye_scara_stationary_cam_approx
  7. 2021年新版电影小程序商业版+前端(含教程、采集)
  8. Hadoop组件搭建-Hadoop HA高可用
  9. 全国31个省市自治区交通事故数、死亡人数等公开数据(1998-2017年)
  10. 如何看懂Apache Pulsar?(究极缝合)