使用Redis实现分布式锁

说明:在多线程情况下,我们需要用到锁来控制线程对资源的访问,当在多线程+分布式的情况下,如果使用synchronized (this),这会在每台服务器实例上都生成一个锁对象,而这个锁只会对当前实例生效,无法对其他服务器实例起作用。

或者,当我们的操作涉及到多数据源的情况,也无法使用synchronized (this),这时就需要使用到分布式锁,将锁从具体实例中抽出来,放在一个公共的地方

分布式锁,可以通过Redis或者Zookeeper来实现,本文介绍使用Redis实现分布式锁,以及Redisson的使用。

Demo

首先,搭建一个简单的Demo,有get和set两个接口;

(controller)

import com.hezy.pojo.Student;
import com.hezy.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/demo")
public class DemoController {@Autowiredprivate DemoService demoService;@GetMapping("/get")public String get() {return demoService.get();}@GetMapping("/set")public String set(Student student) {return demoService.set(student);}
}

set接口,用于接收一个Student对象,并存入到Redis中;

    @Overridepublic String set(Student student) {redisTemplate.opsForValue().set("student", student);return "success";}

get接口,用于从Redis中读取对象,如果没有则从数据库中读取,并存入到Redis中;

    @Overridepublic String get() {// 访问redis缓存Student student = (Student) redisTemplate.opsForValue().get("student");// 如果缓存中有数据,直接返回if (student != null) {System.out.println("从缓存中获取数据");return student.toString();} else {synchronized (this){System.out.println("从数据库中获取数据");student = new Student("hezy", 18);this.set(student);return "success";}}}

(Service实现类)

import com.hezy.pojo.Student;
import com.hezy.service.DemoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;@Service
public class DemoServiceImpl implements DemoService {@Autowiredprivate RedisTemplate redisTemplate;@Overridepublic String get() {// 访问redis缓存Student student = (Student) redisTemplate.opsForValue().get("student");// 如果缓存中有数据,直接返回if (student != null) {System.out.println("从缓存中获取数据");return student.toString();} else {synchronized (this){System.out.println("从数据库中获取数据");student = new Student("hezy", 18);this.set(student);return "success";}}}@Overridepublic String set(Student student) {redisTemplate.opsForValue().set("student", student);return "success";}
}

(Student对象)

import java.io.Serializable;public class Student implements Serializable {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}

测试

分别对set和get接口进行测试;

(set接口)

在这里插入图片描述

(Redis中存入了数据)

在这里插入图片描述


(get接口)

在这里插入图片描述

多线程测试

先删掉Redis中的缓存;

在这里插入图片描述

使用Apifox对get接口进行多线程访问测试,看下控制台的输出结果;

在这里插入图片描述

执行结束;

在这里插入图片描述

控制台结果,可以发现,开头这里访问了三次数据库,这是为什么?

在这里插入图片描述

这是因为在多线程情况下,访问数据库,存入Redis的操作还没完成,但是其他的线程已经通过了student == null的判断,在等待锁释放后,依次访问数据库,所以导致了这种情况;

在这里插入图片描述

解决方法是在单例模式中使用到的方法,双重检查锁定(Double-Check Locking),如下,在锁的代码块里面再做一层判断;

    public String get() {// 访问redis缓存Student student = (Student) redisTemplate.opsForValue().get("student");// 如果缓存中有数据,直接返回if (student != null) {System.out.println("从缓存中获取数据");return student.toString();} else {synchronized (this){student = (Student) redisTemplate.opsForValue().get("student");if (student != null) {System.out.println("从缓存中获取数据");return student.toString();}System.out.println("从数据库中获取数据");student = new Student("hezy", 18);this.set(student);return "success";}}}

缓存删掉,再跑一遍测试,可以看到这回就只有头一次访问了数据库;

在这里插入图片描述

分布式锁

上面的内容不是本文的重点,使用Redis实现分布式锁,具体是通过Redis的这个命令:

setnx [key] [value]

表示当这个key在Redis不存在时,set才能成功,这不正是锁的机制吗?set成功表示获取锁;

在这里插入图片描述

在RedisTemplate中,是用下面这个方法;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;@SpringBootTest
public class RedisTest {@Autowiredprivate RedisTemplate redisTemplate;@Testpublic void test() {Boolean a = redisTemplate.opsForValue().setIfAbsent("lock", "hezy");System.out.println(a);Boolean b = redisTemplate.opsForValue().setIfAbsent("lock", "hezy");System.out.println(b);}
}

执行结果;

在这里插入图片描述

可以将上面get()接口的代码改造一下,加上分布式锁,同时在finally代码块中释放分布式锁;

    public String get() {// 访问redis缓存Student student = (Student) redisTemplate.opsForValue().get("student");// 如果缓存中有数据,直接返回if (student != null) {System.out.println("从缓存中获取数据");return student.toString();} else {// 获取分布式锁Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", "hezy");if (lock) {try {student = (Student) redisTemplate.opsForValue().get("student");if (student != null) {System.out.println("从缓存中获取数据");return student.toString();}System.out.println("从数据库中获取数据");student = new Student("hezy", 18);this.set(student);return "success";} finally {// 释放锁redisTemplate.delete("lock");}}return "";}}

这就是Redis实现分布式锁的方式,但是仔细分析就会发现这种方式有问题:

如果在try代码块里面有BUG,程序中断,导致finally代码块没有执行,锁无法被释放怎么办?可以考虑给分布式锁设置一个有效时间,到了时间自动释放。

但是时间要设置多少?设置过短,程序没有执行完,有效期到了锁自动释放,其他线程获取锁,然后原来的线程执行完,不就把其他线程的分布式锁给释放了吗?凉凉;设置太长,又会导致程序中断,分布式锁需要较长的时间才能被释放,影响性能;

Redisson

为了解决原生Redis方法实现分布式锁的问题,可以考虑使用Redisson,如下:

(导入依赖)

	<!--redisson依赖--><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.11.2</version></dependency>

(装配对象)

    @Autowiredprivate RedissonClient redissonClient;

(改造代码)

    @Overridepublic String get() {// 访问redis缓存Student student = (Student) redisTemplate.opsForValue().get("student");// 如果缓存中有数据,直接返回if (student != null) {System.out.println("从缓存中获取数据");return student.toString();} else {// 获取分布式锁RLock lock = redissonClient.getLock("lock");// 尝试获取锁lock.lock();try {student = (Student) redisTemplate.opsForValue().get("student");if (student != null) {System.out.println("从缓存中获取数据");return student.toString();}System.out.println("从数据库中获取数据");student = new Student("hezy", 18);this.set(student);return "success";} finally {// 释放锁lock.unlock();}}}

Redisson底层实现原理是,当线程获取到锁时,会执行lua脚本,将锁写入到Redis中,获取锁失败时,会调用while循环,不断尝试获取锁;而当线程在获取锁后,执行失败会自动释放锁,默认是30秒,而如果锁时间过期,程序还在执行,会启动一个看门狗(Watch Dog)后台线程,不断给锁续期,直至程序执行完成。

以上就是Redis分布式锁的实现,以及Redisson的使用;

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

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

相关文章

【数字图像处理】边缘检测

边缘检测是一种图像处理技术&#xff0c;用于在图像中识别和提取物体边缘的信息&#xff0c;广泛应用于计算机视觉和图像分析领域。本文主要介绍数字图像边缘检测的基本原理&#xff0c;并记录在紫光同创 PGL22G FPGA 平台的布署与实现过程。 目录 1 边缘检测原理 2 FPGA 布署…

【工具分享】| 阅读论文神器 使用技巧 AI润色 AI翻译

文章目录 1 使用技巧1.1 功能一 即时翻译1.2 功能二 文献跳转1.3 功能三 多设备阅读1.4 功能四 小组讨论笔记共享1.5 功能五 个人文献管理 2 其他功能 超级喜欢Readpaper这一款论文阅读软件&#xff0c;吹爆他哈哈 为什么&#xff1f; 当然是他可以解决我们传统阅读论文的种种…

数据库范式1NF-4NF

码和属性 字段是对内而言的&#xff0c;private的 属性是对外而言的&#xff0c;public的 用Java中的类比喻就是一个对像里面定义了很多字段&#xff0c;一般情况下每个字段都有一组对应的getter&setter方法&#xff0c;注意到了吗&#xff0c;字段一般用private修饰&#…

HarmonyOs 4 (一) 认识HarmonyOs

目录 一 HarmonyOs 背景1.1 发展时间线1.2 背景分析1.2.1 新场景1.2.2 新挑战1.2.3 鸿蒙生态迎接挑战 二 HarmonyOS简介2.1 OpenHarmony2.2 HarmonyOS Connect2.3 HarmonyOS Next**2.4 ArkTS &#xff08;重点掌握&#xff09;****2.5 ArkUI** 三 鸿蒙生态应用核心技术理念**3.…

探索APP自动化测试工具的重要作用是什么?

随着移动应用市场的蓬勃发展&#xff0c;保障应用程序的质量和性能成为开发团队至关重要的任务。在这个背景下&#xff0c;APP自动化测试工具崭露头角&#xff0c;成为提高开发效率、减少错误率的关键工具。本文将探讨APP自动化测试工具的用途&#xff0c;以及它们在移动应用开…

mongoDB非关系型数据库学习记录

一、简介 1.1Mongodb是什么 MongoDB是一个基于分布式文件存储的数据库,官方地址https://www.mongodb.com/ 1.2数据库是什么 数据库(DataBase)是按照数据结构来组织、存储和管理数据的应用程序 1.3数据库的作用 数据库的主要作用就是管理数据,对数据进行增©、删(d)、…

大语言模型(LLMs)在 Amazon SageMaker 上的动手实践(一)

本期文章&#xff0c;我们将通过三个动手实验从浅到深地解读和演示大语言模型&#xff08;LLMs&#xff09;&#xff0c;如何结合 Amazon SageMaker 的模型部署、模型编译优化、模型分布式训练等。 实验一&#xff1a;使用 Amazon SageMaker 构建基于开源 GPT-J 模型的对话机器…

C语言错误处理之 “strerror和perror函数以及断言处理方式”

目录 前言 perror函数 strerror函数 断言处理方式 前言 在错误处理一中&#xff0c;我们解释了C语言三种处理方式中的错误号处理方式&#xff0c;这一篇我们在基于上一篇的基础上加入了strerror函数与perror函数&#xff0c;以及断言处理方式的内容...... perror函数 包…

VSCode下载安装教程+安装插件

一、vscode下载安装 1.打开 官网&#xff1a;https://code.visualstudio.com/Download 2.选择跟你电脑相对应的版本下载&#xff0c;我是win10&#xff0c;所以选择如下&#xff1a; 3.下载到你想要保存的目录下 4.下载完成后打开目录&#xff0c;双击安装包 5. 同意&#xff…

使用YOLOv8训练自己的数据集

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 拉取项目 git clone https://github.com/ultralytics/ultralytics安装依赖 cd ultralytics pip install -r requirement.txt pip instal…

【android开发-10】android中四种布局详细介绍

在Android开发中&#xff0c;常见的四种布局分别是&#xff1a;线性布局&#xff08;LinearLayout&#xff09;、相对布局&#xff08;RelativeLayout&#xff09;、帧布局&#xff08;FrameLayout&#xff09;和绝对布局&#xff08;AbsoluteLayout&#xff09;。 注意&#…

第16届中国R会议暨2023X-AGI大会开幕,和鲸科技分享ModelOps在数据科学平台中的实践与应用

11月25日&#xff0c;第 16 届中国 R 会议暨 2023 X-AGI 大会在在中国人民大学逸夫会堂拉开帷幕&#xff0c;本次会议由中国人民大学统计学院、中国人民大学应用统计科学研究中心、统计之都、原灵科技和中国商业统计学会人工智能分会&#xff08;筹&#xff09;主办&#xff0c…

React项目使用NProgress作为加载进度条

React项目使用NProgress作为加载进度条 0、效果1、react安装依赖2、使用3.进度条颜色设置 文档参考&#xff1a;https://zhuanlan.zhihu.com/p/616245086?utm_id0 0、效果 如下&#xff0c;可全局在页面顶部有一条进度条 1、react安装依赖 yarn add nprogress通过以上安装…

pytest自动化框架之allure测试报告的用例描述设置

allure测试报告的用例描述相关方法&#xff1b;如下图 allure标记用例级别severity 在做自动化测试的过程中&#xff0c;测试用例越来越多的时候&#xff0c;如果执行一轮测试发现了几个测试不通过&#xff0c;我们也希望能快速统计出缺陷的等级。 pytest结合allure框架可以对…

YOLOv5项目实战(5)— 算法模型优化和服务器部署

前言:Hello大家好,我是小哥谈。近期,作者所负责项目中的算法模型检测存在很多误报情况,为了减少这种误报情况,作者一直在不断优化算法模型。鉴于此,本节课就给大家详细介绍一下实际工作场景中如何去优化算法模型和进行部署,另外为了方便大家进行模型训练,作者在文章中提…

oracle FUNCTION(任意两个时间 之间的工作小时)

写函数计算 任意两个时间 之间的工作小时 每天工作时间&#xff08;8:00 - 20:00 共12小时&#xff09;&#xff0c;没有休息日 CREATE OR REPLACE FUNCTION SC_YD_DESI.CALCULATE_WORK_HOURS_FUNC (p_current_time IN DATE,p_order_time IN DATE ) RETURN NUMBER ASp_work_hou…

AWS Remote Control ( Wi-Fi ) on i.MX RT1060 EVK - 1 “建立开发环境”

这个系列的文章将叙述如何借由 NXP 的“evkmimxrt1060_aws_remote_control_wifi_nxp”这支 Sample Code&#xff0c;达到 NXP RT1060EVK 经由 U-Blox EVK-JODY-W263 将资讯传到 AWS 上&#xff0c;并可借由手机对 RT1060 EVK 的 LED 进行远端控制。 整体架构如下图所示&#x…

道可云会展元宇宙平台全新升级,打造3D沉浸式展会新模式

随着VR虚拟现实、人工智能、虚拟数字人等元宇宙技术的快速发展&#xff0c;各个行业正试图通过元宇宙技术寻求新的发展突破口&#xff0c;会展行业也不例外。会展作为经贸领域的重要产业形态&#xff0c;越来越多的企业和组织开始寻求通过元宇宙技术为展会赋能&#xff0c;以满…

【EI会议征稿】第七届大数据与应用统计国际学术研讨会(ISBDAS 2024)

第七届大数据与应用统计国际学术研讨会&#xff08;ISBDAS 2024&#xff09; 2024 7th International Symposium on Big Data and Applied Statistics 第七届大数据与应用统计国际学术研讨会&#xff08;ISBDAS 2024&#xff09;定于2024年3月8-10日在中国上海举行。会议旨在…

最小化安装 Neokylin7.0 用于搭建 Hadoop 集群

文章目录 环境搭建背景虚拟机创建和环境配置安装过程注意事项虚拟机设置软件选择KOUMP系统分区网络和主机名打开以太网&#xff0c;并记录信息配置 IPv4修改主机名 创建用户 hadoop完全分布式搭建-CSDN博客 环境搭建背景 为什么不从hadoop100或者hadoop101开始&#xff0c;而是…