详解分布式锁

知识点: 

单体锁存在的问题:

  • 单体锁,即单体应用中的锁,通过加单体锁(synchronized或RentranLock)可以保证单个实例并发安全

  • 单体锁是JVM层面的锁,只能保证单个实例上的并发访问安全

  • 如果将单体应用部署到多个tomcat实例上,由负载均衡将请求分发到不同的实例

  • 每个tomocat实例都是一个JVM进程,多实例下会存在数据一致性问题。

分布式锁:

  • 分布式应用中所有线程都去获取同一把锁,但只有一个线程可以成功的获得锁,其他没有获得锁的线程必须全部等待,直到持有锁的线程释放锁。

  • 分布式锁是可以跨越多个tomcat实例,多个JVM进程的锁,所以分布式锁都是设计在第三方组件中的

  • 分布式锁都是通过第三方组件来实现的,目前主流的解决方案是使用Redis或Zookeeper来实现分布式锁

存在的问题:

出现用户超买,商家超卖的问题
具体案例:

添加相关依赖:

        <!--   redis     --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>

配置application.yml

# 添加redis数据库
spring:redis:port: 6379database: 1host: 127.0.0.1

编写具体实例:

@RestController
@RequiredArgsConstructor
public class LockController {private final StringRedisTemplate redisTemplate;@SneakyThrows@GetMapping("/deductStock")public String deductStock() {System.out.println("用户正在下单……");/*** 单体锁*/int total = Integer.parseInt(redisTemplate.opsForValue().get("stock"));if (total > 0) {total = total - 1;Thread.sleep(3000);redisTemplate.opsForValue().set("stock", String.valueOf(total));System.out.println("下单成功!剩余库存为:" + total);return "下单成功!剩余库存为:" + total;}System.out.println("用户下单失败!");return "下单失败!剩余库存为:" + total;}
}

测试(点击要快):

我们模拟了系统休眠 ,多线程同时进入一个方法体中,此时,票100同时卖给了两个用户!

解决方案:
 

单体锁:

使用synchronized关键字:修改方法或代码块,用于实现同步控制。当一个线程进入synchronized修饰的方法或代码块时,其他线程需要等待该线程执行完毕后才能进入。

 其中,this关键字指的是,该类的具体实例,即LockController类的具体实例:

@RestController
@RequiredArgsConstructor
public class LockController {private final StringRedisTemplate redisTemplate;@SneakyThrows@GetMapping("/deductStock")public String deductStock() {System.out.println("用户正在下单……");/*** 单体锁*/synchronized (this) {int total = Integer.parseInt(redisTemplate.opsForValue().get("stock"));if (total > 0) {total = total - 1;Thread.sleep(3000);redisTemplate.opsForValue().set("stock", String.valueOf(total));System.out.println("下单成功!剩余库存为:" + total);return "下单成功!剩余库存为:" + total;}System.out.println("用户下单失败!");return "下单失败!剩余库存为:" + total;}}
}

测试结果:

虽然单体锁,解决了在同一个类中,多线程进入方法的问题,但是,当LockController并非单例,也会出现超卖现象: 

存在问题:当项目部署到集群服务器中,由反向代理服务器,负载均衡。会导致出现多个LockController实例。

解决方法(使用分布式锁):

分布式锁:

首先创建一个工具类,用于注入静态的组件:

@Component
public class ApplicationContextHolder implements ApplicationContextAware {private static ApplicationContext ac;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.ac = applicationContext;}public static <T> T getBean(Class<T> clazz){return ac.getBean(clazz);}public static Object getBean(String name){return ac.getBean(name);}
}

定义一个工具类,用于获取锁,释放锁:

/*** 分布式锁工具类*/
public class LockUtil {private static StringRedisTemplate redisTemplate = ApplicationContextHolder.getBean(StringRedisTemplate.class);//获取锁的超时时间(自旋重试时间)private static long waitTimeout = 10000L;//锁的过期时间,防止死锁private static long lockTimeout = 10L;/*** 获取分布式锁*/public static boolean getLock(String lockName, String value) {//计算获取锁的超时时间long endTime = System.currentTimeMillis() + waitTimeout;//超时之前尝试获取锁while (System.currentTimeMillis() < endTime) {//判断是否能够获取锁,其实就是判断是否往redis中插入对应的keyBoolean flag = redisTemplate.opsForValue().setIfAbsent(lockName, value, lockTimeout, TimeUnit.SECONDS);if (flag) {return true;}}return false;}/*** 释放分布式锁*/public static void unlock(String lockName, String value) {if(value.equals(redisTemplate.opsForValue().get(lockName))){redisTemplate.delete(lockName);}}
}

使用分布式锁进行加锁:

@RestController
@RequiredArgsConstructor
public class LockController {private final StringRedisTemplate redisTemplate;@SneakyThrows@GetMapping("/deductStock")public String deductStock() {System.out.println(Thread.currentThread().getName() + "用户正在下单……");/*** 分布式锁*/String lockName = "stock_lock";String value = UUID.randomUUID().toString();if (!LockUtil.getLock(lockName, value)) {return "获取锁失败……";}int total = Integer.parseInt(redisTemplate.opsForValue().get("stock"));if (total > 0) {total = total - 1;Thread.sleep(3000);redisTemplate.opsForValue().set("stock", String.valueOf(total));System.out.println("下单成功!剩余库存为:" + total);LockUtil.unlock(lockName,value); //释放锁return "下单成功!剩余库存为:" + total;}System.out.println("用户下单失败!");LockUtil.unlock(lockName,value);return "下单失败!剩余库存为:" + total;}
}

测试结果:

 存在问题:当用户进入后,拿到锁后,执行后续代码,但是锁到期了,锁被释放出来。后续的用户,也是可以进入线程当中的。依旧会出现抄买现象。

解决方法(第三方库来实现分布式锁 ):判断当前用户是否完成后续操作,如果没有完成就自动续签(加时长),直到用户完成后续操作。

Redisson:

Redisson是一个基于Redis的Java驻留对象框架,它提供了一套易于使用的API,用于操作Redis的数据结构和执行分布式操作。

Redisson是Redis官网推荐实现分布式锁的一个第三方类库,用起来更简单。

执行流程:

  • 只要线程加锁成功(默认锁的超时时间为30s),Redisson就会启动一个用于监控锁的看门狗,它是一个守护线程,会每隔10秒检查一下,如果线程还持有锁,就会不断的延长锁的有效期(即每到20s就会自动续借成30s),也称为自动续期机制

  • 当业务执行完,释放锁后,会关闭守护线程。

  • 从而防止了线程业务还没执行完,而锁却过期的问题 。

首先引入相关依赖:

<!-- redisson -->
<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.24.3</version>
</dependency>

编写代码,调用工具类:

@RestController
@RequiredArgsConstructor
public class LockController {private final StringRedisTemplate redisTemplate;private final RedissonClient redissonClient;@SneakyThrows@GetMapping("/deductStock")public String deductStock() {System.out.println(Thread.currentThread().getName() + "用户正在下单……");/*** 使用Redisson分布式锁*/String lockName = "stock_lock";RLock rLock = redissonClient.getLock(lockName);rLock.lock(); //获取锁int total = Integer.parseInt(redisTemplate.opsForValue().get("stock"));if (total > 0) {total = total - 1;Thread.sleep(3000);redisTemplate.opsForValue().set("stock", String.valueOf(total));System.out.println("下单成功!剩余库存为:" + total);rLock.unlock();return "下单成功!剩余库存为:" + total;}System.out.println("用户下单失败!");rLock.unlock();return "下单失败!剩余库存为:" + total;}
}

测试结果:

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

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

基于springboot实现疾病防控综合系统项目【项目源码+论文说明】

基于springboot实现疾病防控综合系统演示 摘要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&…

浅谈云计算资源和服务

目录 前言 正文 专有名词及其首字母缩写 轻量级应用服务器 云服务器ECS 专有网络VPC 其他类服务 尾声 &#x1f52d; Hi,I’m Pleasure1234&#x1f331; I’m currently learning Vue.js,SpringBoot,Computer Security and so on.&#x1f46f; I’m studying in University o…

如何在您的WordPress网站上安装和设置W3 Total Cache

本周有一个客户&#xff0c;购买Hostease的虚拟主机&#xff0c;询问我们的在线客服&#xff0c;如何在您的WordPress网站上安装和设置W3 Total Cache&#xff1f;我们为用户提供相关教程&#xff0c;用户很快解决了遇到的问题。在此&#xff0c;我们分享这个操作教程&#xff…

Agent AI智能体的未来:无限可能

文章目录 终结者智能体正反影响自我意识开放心态 终结者 还记得那场人类与天网之间的史诗般的战斗吗&#xff1f;-- 《终结者》系列电影。 《终结者》系列电影是一部标志性的科幻动作系列&#xff0c;以紧张刺激的情节、令人难忘的角色和开创性的视觉效果而闻名。 电影探讨了…

【智能优化算法】矮猫鼬优化算法(Dwarf Mongoose Optimization Algorithm,DMHO)

矮猫鼬优化算法(Dwarf Mongoose Optimization Algorithm,DMHO)是期刊“COMPUTER METHODS IN APPLIED MECHANICS AND ENGINEERING”&#xff08;IF 7.3&#xff09;的2022年智能优化算法 01.引言 矮猫鼬优化算法(Dwarf Mongoose Optimization Algorithm,DMHO)模仿矮猫鼬的觅食行…

天府锋巢直播产业基地构建成都电商直播高地

天府锋巢直播产业基地自成立以来&#xff0c;一直秉承着创新、协同、共赢的发展理念&#xff0c;吸引了众多直播企业纷纷入驻。随着直播产业的迅猛发展&#xff0c;改成都直播基地内的配套服务也显得尤为重要。本文将深入探讨入驻天府锋巢直播产业基地后&#xff0c;配套的直播…

找不到msvcp140.dll无法执行代码的原因分析及修复方法

当用户在尝试运行某些应用程序或游戏时&#xff0c;可能会遇到系统弹出错误提示&#xff0c;显示“找不到msvcp140.dll无法执行代码”这一错误信息&#xff0c;它会导致程序无法正常启动。为了解决这个问题&#xff0c;我经过多次尝试和总结&#xff0c;找到了以下五种解决方法…

第十三届蓝桥杯决赛(国赛)真题 Java C 组【原卷】

文章目录 发现宝藏试题 A: 斐波那契与 7试题 B: 小蓝做实验试题 C: 取模试题 D: 内存空间试题 E \mathrm{E} E : 斐波那契数组试题 F: 最大公约数试题 G: 交通信号试题 I: 打折试题 J: 宝石收集 发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#x…

如何在 CentOS 上安装并配置 Redis

如何在 CentOS 上安装并配置 Redis 但是太阳&#xff0c;他每时每刻都是夕阳也都是旭日。当他熄灭着走下山去收尽苍凉残照之际&#xff0c;正是他在另一面燃烧着爬上山巅散烈烈朝晖之时。 ——史铁生 环境准备 本教程将在 CentOS 7 或 CentOS 8 上进行。确保你的系统已更新到最…

【数据结构】队列详解(Queue)

文章目录 有关队列的概念队列的结点设计及初始化队列的销毁判空和计数入队操作出队操作 有关队列的概念 队列:只允许在一端进行插入数据操作&#xff0c;在另一端进行删除数据操作的特殊线性表&#xff0c;队列具有先进先出FIFO(First In First Out)入队列:进行插入操作的一端…

【C++】详细版 RAII技术的应用之智能指针(智能指针发展历程和简单模拟实现介绍)

目录 前言 一、智能指针有什么用&#xff1f; 二、什么是RAII(智能指针的底层思想)&#xff1f; 三、智能指针的发展历程以及模拟实现 1.auto_ptr&#xff08;C98&#xff09; 2.unique_ptr&#xff08;C11&#xff09; 3.shared_ptr&#xff08;C11&#xff09; 前言 C中…

Linux修炼之路之初识操作系统+基础指令(1)

目录 引言 一&#xff1a;对操作系统(OS)的简单了解 1.操作系统(OS) 是什么 2.操作系统好坏的衡量标准 3.操作系统存在的重要性 4.理解所有在计算机上的操作 二&#xff1a;Linux与windows操作的特点区别 三&#xff1a;基础指令 1.ls 指令 1.使用 2.常用选项 2.…

2024-AIDD-人工智能药物设计-AlphaFold3

AlphaFold3&#xff5c;万字长文解读 AlphaFold3预测所有分子相互作用准确结构 AlphaFold3 自2021年AlphaFold2问世以来&#xff0c;科研工作者们便开始利用这一蛋白结构预测模型来详细描绘众多蛋白质的结构、探索新药。近日&#xff0c;Google DeepMind公司推出了其最新产品…

关于JAVA-JSP电子政务网实现参考论文(论文 + 源码)

【免费】关于JAVA-JSP电子政务网.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89292355关于JAVA-JSP电子政务网 摘 要 当前阶段&#xff0c;伴随着社会信息技术的快速发展&#xff0c;使得电子政务能够成为我国政府职能部门进行办公管理的一个重要内容&#x…

日本OTC机械手维修需要注意哪些问题呢?

随着工业4.0时代的到来&#xff0c;机器人在制造业中的应用越来越广泛。OTC&#xff08;Over The Counter&#xff09;机器人作为工业机器人的一种&#xff0c;以其高效、精准、稳定的特点受到众多企业的青睐。然而&#xff0c;在实际使用过程中&#xff0c;可能会出现一些OTC机…

每日一题——力扣27. 移除元素(举一反三)

题目链接&#xff1a;https://leetcode.cn/problems/remove-element/description/ 菜鸡写法&#xff1a; // 函数定义&#xff0c;移除数组nums中所有值为val的元素&#xff0c;并返回新的数组长度 int removeElement(int* nums, int numsSize, int val) {// 如果数组长度为…

【智能优化算法】金豺狼优化算法(Golden jackal optimization,GJO)

金豺狼优化(Golden jackal optimization,GJO)是期刊“Expert Systems with Applications”&#xff08;中科院一区IF 8.3&#xff09;的2022年智能优化算法 01.引言 金豺狼优化(Golden jackal optimization,GJO)旨在为解决实际工程问题提供一种替代的优化方法。GJO的灵感来自金…

c++:(map和set的底层简单版本,红黑树和AVL树的基础) 二叉搜索树(BST)底层和模拟实现

文章目录 二叉搜索树的概念二叉搜索树的操作二叉搜索树的查找find 二叉搜索树的模拟实现构造节点insertfinderase(细节巨多,面试可能会考)a.叶子节点b.有一个孩子左孩子右孩子 c.有两个孩子注意: erase代码 中序遍历 二叉搜索树的应用k模型k模型模拟实现的总代码 k-value模型k-…

高校教务选课管理系统开发方案

一、项目背景与目标 &#xff08;一&#xff09;项目背景 随着高校教育规模的扩大&#xff0c;教务管理变得越来越复杂&#xff0c;传统的手工管理方式已经无法满足现代高校的需求。因此&#xff0c;开发一套高效、便捷的高校教务选课管理系统显得尤为重要。该系统将涵盖学生…

基于Springboot+Vue的Java项目-车辆管理系统开发实战(附演示视频+源码+LW)

大家好&#xff01;我是程序员一帆&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &am…