大数据-50 Redis 分布式锁 乐观锁 Watch SETNX Lua Redisson分布式锁 Java实现分布式锁

点一下关注吧!!!非常感谢!!持续更新!!!

目前已经更新到了:

  • Hadoop(已更完)
  • HDFS(已更完)
  • MapReduce(已更完)
  • Hive(已更完)
  • Flume(已更完)
  • Sqoop(已更完)
  • Zookeeper(已更完)
  • HBase(已更完)
  • Redis (正在更新…)

章节内容

上节我们完成了:

  • Redis缓存相关的概念
  • 缓存穿透、缓存击穿、数据不一致性等
  • HotKey、BigKey等问题
  • 针对上述问题提出一些解决方案

在这里插入图片描述

分乐观锁介绍

乐观锁基于CAS(Compare And Swap)思想,比较和替换,是不具有互斥性,不会产生锁等待而消耗资源,但需要反复的重试,能比较快的响应。

Watch实现

watch介绍

我们可以使用 Redis 来实现乐观锁:

  • 利用 Redis 的 watch 功能,监控 Redis-Key的状态值
  • 获取 RedisKey 的值
  • 创建 Redis 事务
  • 给这个Key的值+1
  • 然后去执行这个事务,如果 key 的值被修改过则修改,key不加1

wacth实现

暂时就先忽略编码规范的内容,就先实现即可。
具体编写逻辑如下:

public class Test02 {public static void main(String[] args) {String redisKey = "lock";ExecutorService executor = Executors.newFixedThreadPool(20);try {Jedis jedis = new Jedis("h121.wzk.icu", 6379);jedis.del(redisKey);jedis.set(redisKey, "0");jedis.close();} catch (Exception e) {e.printStackTrace();}for (int i = 0; i < 300; i ++) {executor.execute(() -> {Jedis jedis = null;try {jedis = new Jedis("h121.wzk.icu", 6379);jedis.watch(redisKey);String redisValue = jedis.get(redisKey);int value = Integer.valueOf(redisValue);String userInfo = UUID.randomUUID().toString();if (value < 20) {Transaction tx = jedis.multi();tx.incr(redisKey);List<Object> list = tx.exec();if (list != null && !list.isEmpty()) {System.out.println("获取锁成功, 用户信息: " + userInfo + " 成功人数: " + (value + 1));}} else {System.out.println("秒杀结束!");}} catch (Exception e) {e.printStackTrace();} finally {if (null != jedis) {jedis.close();}}});}executor.shutdown();}}

运行之后,会看到已经在进行争抢了:

获取锁成功, 用户信息: e6e06770-f274-4d89-8369-65babc2e3073 成功人数: 1
获取锁成功, 用户信息: 2cc2803b-085e-47ee-9fe6-4bbe1f694fd5 成功人数: 2
获取锁成功, 用户信息: 525ad22c-abb2-4f94-868a-cca981f9d768 成功人数: 3
获取锁成功, 用户信息: 9af67396-798e-4e09-b524-6ddc5e1673ec 成功人数: 4
获取锁成功, 用户信息: d5aa82f4-7d25-42c1-b8db-01ff7cfaf6c6 成功人数: 5
获取锁成功, 用户信息: 7dcc0646-e7a0-4cc0-bdcc-b96c7e8ba98b 成功人数: 6
获取锁成功, 用户信息: 7c9276d0-eec9-462a-8a8b-87711406375b 成功人数: 8
获取锁成功, 用户信息: c43b0158-b211-4a91-b430-51eb6ef74ded 成功人数: 9
获取锁成功, 用户信息: 9ab9418f-5e52-4d28-9ea5-92bc6b8b7742 成功人数: 7
获取锁成功, 用户信息: 7692d829-f7ef-4e28-90a4-2222a14c45d4 成功人数: 11
获取锁成功, 用户信息: 52695f97-49bf-4a06-bc45-a8ee1abb4524 成功人数: 10
获取锁成功, 用户信息: 196e29cc-b2fe-4356-841c-1f4376e3d5ae 成功人数: 12
获取锁成功, 用户信息: 8bb39e3c-c751-4468-b948-50ccb6aeb533 成功人数: 13
获取锁成功, 用户信息: d9691236-13f0-452b-b765-bc15b094866b 成功人数: 14
获取锁成功, 用户信息: cb1b0291-de78-4779-b4e6-294121393e9f 成功人数: 15
获取锁成功, 用户信息: dc368684-533f-47b0-9847-3fbfbf8fee78 成功人数: 16
获取锁成功, 用户信息: 361d2d66-cb9d-4e79-9c85-19f1b83c136d 成功人数: 17
获取锁成功, 用户信息: bd6fe63f-e48a-48f1-b751-e091d19886a2 成功人数: 19
秒杀结束!
秒杀结束!
秒杀结束!
秒杀结束!
获取锁成功, 用户信息: dba287f8-65f0-4da8-a131-05304164b3aa 成功人数: 18
秒杀结束!
获取锁成功, 用户信息: 05c5c5f9-f9cd-48b3-a266-c4ff3f256814 成功人数: 20
秒杀结束!
秒杀结束!
秒杀结束!

SETNX

setnx介绍

  • 共享资源互斥
  • 共享资源串行化
  • 单应用中使用锁:单进程但是多线程
  • synchronized、ReentrantLock
  • 分布式应用中的锁:多进程多线程
  • 分布式锁是控制分布式系统之间同步访问共享资源的一种方式
  • 利用Redis的单线程特性对共享资源进行串行化处理

SETNX实现

获取锁方式1 SET

public boolean getLock(String lockKey,String requestId,int expireTime) {// NX:保证互斥性// hset 原子性操作 只要lockKey有效 则说明有进程在使用分布式锁String result = jedis.set(lockKey, requestId, "NX", "EX", expireTime);if("OK".equals(result)) {return true;}return false;
}

获取锁方式2 SETNX

public boolean getLock(String lockKey,String requestId,int expireTime) {Long result = jedis.setnx(lockKey, requestId);if(result == 1) {// 成功设置 进程down 永久有效 别的进程就无法获得锁jedis.expire(lockKey, expireTime);return true;}return false;
}

释放锁方式1 del

注意,当调用del方法时候,如果这把锁已经不属于当前客户端了,比如已经过期了,而别的人拿到了这把锁,此时删除就会导致释放掉了别人的锁。

public static void releaseLock(String lockKey,String requestId) {if (requestId.equals(jedis.get(lockKey))) {jedis.del(lockKey);}
}

释放锁方式2 lua

public static boolean releaseLock(String lockKey, String requestId) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then returnredis.call('del', KEYS[1]) else return 0 end";Object result = jedis.eval(script, Collections.singletonList(lockKey),Collections.singletonList(requestId));if (result.equals(1L)) {return true;}return false;
}

Redisson分布式锁

Redisson介绍

  • Redisson是假设在Redis基础上的Java驻内存数据网格(In-Memory Data Grid)
  • Redisson是基于NIO的Netty框架上,生产环境使用分布式锁。

添加依赖

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

配置Redisson

public class RedissonManager {private static final Config CONFIG = new Config();private static Redisson redisson = null;static {CONFIG.useClusterServers().setScanInterval(2000).addNodeAddress("redis://h121.wzk.icu:6379").addNodeAddress("redis://h122.wzk.icu:6379").addNodeAddress("redis://h123.wzk.icu:6379");redisson = (Redisson) Redisson.create(CONFIG);}public static Redisson getRedisson() {return redisson;}}

获取与释放锁

public class DistributedRedisLock {private static Redisson redisson = RedissonManager.getRedisson();private static final String LOCK_TITLE = "redisLock_";public static boolean acquire(String lockName) {String key = LOCK_TITLE  + lockName;RLock rLock = redisson.getLock(key);rLock.lock(3, TimeUnit.SECONDS);return true;}public static void release(String lockName) {String key = LOCK_TITLE  + lockName;RLock rLock = redisson.getLock(key);rLock.unlock();}}

业务使用

public String discount() throws IOException{String key = "lock001";// 加锁DistributedRedisLock.acquire(key);// 执行具体业务逻辑dosoming// 释放锁DistributedRedisLock.release(key);// 返回结果return soming;
}

实现原理

在这里插入图片描述

分布式锁特性

  • 互斥性:任意时刻,只能有一个客户端获取锁,不能同时有两个客户端获取到锁。
  • 同一性:锁只能被持有该锁客户端删除,不能由其他客户端删除
  • 可重入性:持有某个客户端可持续对该锁加锁 实现锁的续租
  • 容错性:超过生命周期会自动进行释放,其他客户端可以获取到锁

常见分布式锁对比

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/50742.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Docker+consul容器服务的更新与发现

1、Consul概述 &#xff08;1&#xff09;什么是服务注册与发现 服务注册与发现是微服务架构中不可或缺的重要组件。起初服务都是单节点的&#xff0c;不保障高可用性&#xff0c;也不考虑服务的压力承载&#xff0c;服务之间调用单纯的通过接口访问。直到后来出现了多个节点…

Spark实时(三):Structured Streaming入门案例

文章目录 Structured Streaming入门案例 一、Scala代码如下 二、Java 代码如下 三、以上代码注意点如下 Structured Streaming入门案例 我们使用Structured Streaming来监控socket数据统计WordCount。这里我们使用Spark版本为3.4.3版本&#xff0c;首先在Maven pom文件中导…

在 Spring Boot 中使用 Filters 实现请求过滤和预处理

​ 博客主页: 南来_北往 系列专栏&#xff1a;Spring Boot实战 什么是过滤器 过滤器&#xff08;Filter&#xff09;是一种在Web应用中用于拦截和处理HTTP请求和响应的对象。 在Java Web开发中&#xff0c;过滤器是实现特定功能&#xff0c;如认证、日志记录和字符编码处…

X-AnyLabeling标注软件使用方法

第一步 下载 官方X-AnyLabeling下载地址 github&#xff1a;X-AnyLabeling 第二步 配置环境 使用conda创建新的虚拟环境 conda create -n xanylabel python3.8进入环境 conda activate xanylabel进入X-AnyLabeling文件夹内&#xff0c;运行下面内容 依赖文件系统环境运行环…

MyBatisPlus复习

目录 自定义sql swagger工具 IService批量新增 代码生成器 DB静态工具&#xff0c;hutool工具 逻辑删除 枚举处理器 Json处理器 分页 自定义sql swagger工具 IService批量新增 代码生成器 DB静态工具&#xff0c;hutool工具 逻辑删除 枚举处理器 Json处理器 分页

ks滑块验证码逆向分析与python识别

文章目录 1. 写在前面3. 接口分析3. 算法实现 【&#x1f3e0;作者主页】&#xff1a;吴秋霖 【&#x1f4bc;作者介绍】&#xff1a;擅长爬虫与JS加密逆向分析&#xff01;Python领域优质创作者、CSDN博客专家、阿里云博客专家、华为云享专家。一路走来长期坚守并致力于Python…

大模型技术:发展历程、经典模型、微调与应用[更新中...]

文章目录 一、预训练语言模型发展历程二、经典的Pre-trained任务2.1 Masked Language Modeling2.2 Next Sentence Prediction 三、Task-specific Fine-tuning 任务3.1 Single-text Classification (单句分类)3.2 Sentence-pair Classification (句子匹配/成对分类)3.3 Span Tex…

谷粒商城实战笔记-71-商品服务-API-属性分组-前端组件抽取父子组件交互

文章目录 一&#xff0c;一次性创建所有的菜单二&#xff0c;开发属性分组界面1&#xff0c;左侧三级分类树形组件2&#xff0c;右侧分组列表3&#xff0c;左右两部分通信3.1 子组件发送数据3.2&#xff0c;父组件接收数据 Vue的父子组件通信父组件向子组件传递数据子组件向父组…

【BUG】已解决:The above exception was the direct cause of the following exception:

The above exception was the direct cause of the following exception: 目录 The above exception was the direct cause of the following exception: 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎来到我的主页&#xff0c…

【杰理蓝牙开发】AC695x 音频部分

本文主要记录 杰理蓝牙audio接口的使用&#xff0c;包括ADC和DAC原理的介绍和API接口的使用。 【杰理蓝牙开发】AC695x 音频部分 0. 个人简介 && 授权须知1. ADC【音频数据采集】硬件部分1.1 单片机引脚1.2 硬件电路设计1.3 MIC 输入通路解释 2. 【DAC】音频信号编解码…

Super 4PCS配准算法

Nicolas Mellado&#xff0c;CNRS&#xff08;Centre national de la recherche scientifique&#xff0c;法国国家科学研究中心&#xff09;的研究员&#xff0c;在IRIT&#xff08;Institut de Recherche en Informatique de Toulouse&#xff0c;图卢兹计算机科学研究所&…

SAPUI5基础知识20 - 对话框和碎片(Dialogs and Fragments)

1. 背景 在 SAPUI5 中&#xff0c;Fragments 是一种轻量级的 UI 组件&#xff0c;类似于视图&#xff08;Views&#xff09;&#xff0c;但它们没有自己的控制器&#xff08;Controller&#xff09;。Fragments 通常用于定义可以在多个视图中重用的 UI 片段&#xff0c;从而提…

linux系统安装pytorch_中文地址命名实体识别案例

命名实体有关文章参考这篇文章 中文地址命名实体识别训练和预测 win10系统安装cuda环境参考这篇文章 搭建Pytorch的GPU环境超详细 1、下载python https://www.python.org/downloads/release/python-368/ 2、下载python包 https://pypi.org/search/?q=transformers 1、搜…

如何录制电脑内部声音?全方位介绍电脑录音软件:8款在线录音!(2024重新整理)

如何录制电脑内部声音&#xff1f;不管是娱乐圈还是现实生活&#xff0c;【录音】这个功能的重要性不言而喻。而电脑录音已在影视配音、音视频剪辑、会议记录、在线教育等多个领域发光发热&#xff01; 本文将为您推荐8款电脑录音软件&#xff0c;并详细介绍电脑录音的多种方式…

Python番外篇:变量是盒子还是标签

引言 前面通过几十篇文章&#xff0c;大概把Python的一些比较实用的基础做了一些介绍&#xff0c;学会这些&#xff0c;基本能应付日常的小的需求开发了&#xff0c;写一些小工具&#xff0c;提高工作的处理效率。 接下来&#xff0c;准备开始进入一个新的篇章&#xff0c;也…

C#如何引用dll动态链接库文件的注释

1、dll动态库文件项目生成属性中要勾选“XML文档文件” 注意&#xff1a;XML文件的名字切勿修改。 2、添加引用时XML文件要与DLL文件在同一个目录下。 3、如果要是添加引用的时候XML不在相同目录下&#xff0c;之后又将XML文件复制到相同的目录下&#xff0c;需要删除引用&am…

当设计模式牵手LLM

模版方法模式 何为模版设计模式 想象一下 如果我们要泡一杯茶 我们要循序渐进地 煮水温杯注水浸茶茶水入杯加点配料 如此&#xff0c;泡茶的工序就完成了&#xff0c;那么模板方法模式&#xff0c;相信各位也有了一定的概念&#xff1a;定义了一个算法的骨架&#xff0c;而…

UDP的报文结构及其注意事项

1. 概述 UDP&#xff08;User Datagram Protocol&#xff09;是一种无连接的传输层协议&#xff0c;它提供了一种简单的数据传输服务&#xff0c;不保证数据的可靠传输。在网络通信中&#xff0c;UDP通常用于一些对实时性要求较高、数据量较小、传输延迟较低的应用&#xff0c…

【JVM基础07】——类加载器-什么是类加载器?类加载器有哪些?双亲委派了解吗?

目录 1- 引言&#xff1a;类加载器1-1 类加载器是什么&#xff1f;(What)1-2 为什么要用类加载器&#xff1f; 作用&#xff1a;类加载的过程&#xff1f;(Why) 2- ⭐核心&#xff1a;类加载器详解(How)2-1 类加载器分类2-2 什么是双亲委派模型&#xff1f;2-3 为什么采用双亲委…

Pytorch基础:Tensor的squeeze和unsqueeze方法

相关阅读 Pytorch基础https://blog.csdn.net/weixin_45791458/category_12457644.html?spm1001.2014.3001.5482 在Pytorch中&#xff0c;squeeze和unsqueeze是Tensor的一个重要方法&#xff0c;同时它们也是torch模块中的一个函数&#xff0c;它们的语法如下所示。 Tensor.…