当前位置: 首页 > web >正文

黑马点评-实现分布式锁

1.分布式锁实现方案:

注意:使用setnx实现,在之前解决缓存击穿的时候也用了setnx这个命令实现互斥锁。

2.分布式锁实现的思路:

我们在redis中存入一个键值对,每个线程使用setnx命令看能不能设置成功,如果设置成功了则获取锁成功否则就失败线程就不会执行下面的逻辑。释放锁就是把这个键值对删除即可。

3.实现一个简单的分布式锁

简单的分布式锁工具包

public class SimpleRedisLock implements ILock {private static final String KEY_PREFIX = "lock:";private StringRedisTemplate stringRedisTemplate;private String name;public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {this.name = name;this.stringRedisTemplate = stringRedisTemplate;}public boolean tryLock(long timeoutSec) {// 获取线程标示long threadId = Thread.currentThread().getId();// 获取锁Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId + "",timeoutSec, TimeUnit.SECONDS);                           return Boolean.TRUE.equals(success);}public void unlock() {// 释放锁stringRedisTemplate.delete(KEY_PREFIX + name);}
}

这里我们把存入redis中的value设置为线程id后面有大作用。 

使用分布式锁:这只是部分代码

Long userId = UserHolder.getUser().getId();
//创建锁对象(新增代码)
SimpleRedisLock lock = new SimpleRedisLock("order:" + userId, stringRedisTemplate);
//获取锁对象
boolean isLock = lock.tryLock(1200);
//加锁失败
if (!isLock) {return Result.fail("不允许重复下单");
}
try {// TODO 获取代理对象(事务),这样事务才会生效IVoucherOrderService proxy = (IVoucherOrderService) AopContext.currentProxy();return proxy.createVoucherOrder(voucherId);
} finally {//释放锁lock.unlock();
}

出现的问题:线程一释放了线程二的锁

原理图

4.优化分布式锁-解决线程自己释放自己设置的锁

代码实现:

工具包代码优化如下,引入UUID和线程id作为value的值,来检测是不是同一个线程

这里要添加UUID的原因是:两个jvm容易出现线程id相同的情况,所以这里不同的java程序用不同的UUID,以防止线程一删除线程二的锁

public class SimpleRedisLock implements ILock {private static final String KEY_PREFIX = "lock:";private static final String ID_PREFIX = UUID.randomUUID().toString(true) + "-";// 这里要添加UUID的原因是:两个jvm容易出现线程冲突的情况的,所以这里不同的java程序有不同的UUID标示吗,以防止线程一删除线程二的锁private StringRedisTemplate stringRedisTemplate;private String name;public SimpleRedisLock(String name, StringRedisTemplate stringRedisTemplate) {this.name = name;this.stringRedisTemplate = stringRedisTemplate;}public boolean tryLock(long timeoutSec) {// 获取线程标示String threadId = ID_PREFIX + Thread.currentThread().getId();// 获取锁Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(KEY_PREFIX + name, threadId,timeoutSec, TimeUnit.SECONDS);return Boolean.TRUE.equals(success);}public void unlock() {// 获取线程标示String threadId = ID_PREFIX + Thread.currentThread().getId();// 获取锁中的标示String id = stringRedisTemplate.opsForValue().get(KEY_PREFIX + name);// 判断标示是否一致if(threadId.equals(id)) {// 释放锁stringRedisTemplate.delete(KEY_PREFIX + name);}}
}

出现的问题:因为上面的代码判断是否一致和释放锁不是原子性的操作可能期间有阻塞导致错误的释放了别的线程的锁。

原理图:

5.使用lua脚本保证操作的原子性

这里使用lua脚本保证操作的原子性,这里不讲

6.上面分布式锁还存在的问题

7.使用Redisson(官方实现好的分布式锁)

①引入依赖

<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.6</version>
</dependency>

②配置Redisson客户端

@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient(){// 配置Config config = new Config();config.useSingleServer().setAddress("redis://192.168.150.101:6379").setPassword("123321");// 创建RedissonClient对象return Redisson.create(config);}
}

③使用Demo

@Resource
private RedissionClient redissonClient;@Test
void testRedisson() throws Exception{//获取锁(可重入),指定锁的名称RLock lock = redissonClient.getLock("anyLock");//尝试获取锁,参数分别是:获取锁的最大等待时间(期间会重试),锁自动释放时间,时间单位boolean isLock = lock.tryLock(1,10,TimeUnit.SECONDS);//判断获取锁成功if(isLock){try{System.out.println("执行业务");          }finally{//释放锁lock.unlock();}}
}
http://www.xdnf.cn/news/8364.html

相关文章:

  • dify多实例部署,一台机器部署多个dify实例
  • 日语学习-日语知识点小记-构建基础-JLPT-N4阶段(28):ばかり
  • CASIA-HWDB的gnt转换为png图片
  • R语言学习--Day07--T分布与T检验
  • word设置如“第xx页 共xx页”格式的页码
  • OPC Client第5讲(wxwidgets):初始界面的事件处理;按照配置文件初始化界面的内容
  • 【Django DRF】一篇文章总结Django DRF框架
  • 鸿蒙Ability对比Android的Fragment
  • uniapp编译小程序,不支持:class语法
  • 机器学习第二十五讲:TensorFlow → 乐高式搭建深度学习模型
  • kafka吞吐量提升总结
  • halcon 连接相机
  • 消息队列RabbitMQ与AMQP协议详解
  • oracle数据库生成awr报告,排查数据库服务器CPU100%,系统卡顿,慢sql,根据sqlid查询关键信息,如会话SID,客户端机器名
  • 从零搭建SpringBoot Web单体项目3、SpringBoot 核心组件深度解析
  • leetcode hot100:十三、解题思路大全:多维动态规划(不同路径、最小路径和、最长回文子串、 最长公共子序列、编辑距离)
  • 微信小程序用<web-view 嵌入h5网页,改了h5网页后,可能是缓存的原因,小程序上看还是原来的,怎么处理
  • 【MySQL成神之路】MySQL索引相关介绍
  • 应届本科生简历制作指南
  • MySQL数据 在 磁盘上是什么样子的
  • DiagramJS设计原理解读(二)
  • CUDA 加速的基础线性代数库cuBLAS
  • Issac Lab安装
  • SPL做量化---MFI(资金流量指标)
  • 水陆两栖车,水域救援与陆地行动的桥梁
  • 掌握正则表达式:从基础语法到工程实践
  • Redis--SpringDataRedis详解
  • KCTF-CCG CrackMe crypto 1.0
  • TDengine 高可用——三副本
  • YOLOv5:调用官方权重进行检测