JAVA RedisTemplate实现(加锁/解锁) 解决高并发问题

article/2025/10/7 1:06:06

基于传统的单机模式下的并发锁,已远远不能满足当下高并发大负载的情况,当下常用的并发处理如下

       1、使用synchronized关键字

        2、select    for update   乐观锁

        3、使用redis实现同步锁

方案一 适合单机模式,

方案二 虽然满足多节点服务实例但 对变更操作的吞吐量有影响

方案三 基于redis nosql数据库  在效率与横向扩展方面都大大优于前两种方案

redis  单线程   在自身设计上一定程度可避免想成不安全  再者其效率高于关系型数据库

本次实现锁机制  基于redis 的两个指令 查询 redis.cn  网站  

指令一:SETNX key value

key设置值为value,如果key不存在,这种情况下等同SET命令。 当key存在时,什么也不做。SETNX是”SET if Not eXists”的简写。

返回值

Integer reply, 特定值:

  • 1 如果key被设置了
  • 0 如果key没有被设置

 指令二:GETSET key value

自动将key对应到value并且返回原来key对应的value。如果key存在但是对应的value不是字符串,就返回错误。

返回值

bulk-string-reply: 返回之前的旧值,如果之前Key不存在将返回nil

步骤一, pom文件

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId><version>2.6.0</version></dependency>

步骤二,RedisCacheAutoConfiguration

package com.wh.whcloud.df.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wh.whcloud.core.redis.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;/*** Redis缓存配置** @author */
@Slf4j
@Configuration
@AutoConfigureAfter({RedisAutoConfiguration.class})
public class RedisCacheAutoConfiguration {/*** 重新配置一个RedisTemplate** @param factory* @return*/@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory factory) {RedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(factory);Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);RedisSerializer<String> stringSerializer = new StringRedisSerializer();// key采用String的序列化方式template.setKeySerializer(stringSerializer);// hash的key也采用String的序列化方式template.setHashKeySerializer(stringSerializer);// value序列化方式采用jacksontemplate.setValueSerializer(jackson2JsonRedisSerializer);// hash的value序列化方式采用jacksontemplate.setHashValueSerializer(jackson2JsonRedisSerializer);template.setDefaultSerializer(jackson2JsonRedisSerializer);log.info("RedisTemplate配置 [{}]", template);return template;}@Bean@ConditionalOnMissingBean(RedisUtil.class)@ConditionalOnBean(RedisTemplate.class)public RedisUtil redisUtils(RedisTemplate redisTemplate) {RedisUtil redisUtil =   new RedisUtil(redisTemplate);log.info("RedisUtil [{}]", redisUtil);return redisUtil;}
}

步骤三, RedisLock 加解锁实现

package com.wh.whcloud.util;import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import java.util.Collections;
import java.util.concurrent.TimeUnit;public class RedisLock {private static final Long SUCCESS = 1L;private long timeout = 9999; //获取锁的超时时间/*** 加锁,无阻塞** @param* @param* @return*/public static Boolean tryLock(RedisTemplate redisTemplate, String key, String value, long expireTime) {try {//SET命令返回OK ,则证明获取锁成功Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.MILLISECONDS);return ret;} catch (Exception e) {e.printStackTrace();return false;}return false;}/*Long start = System.currentTimeMillis();for(;;){//SET命令返回OK ,则证明获取锁成功Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);return ret;//否则循环等待,在timeout时间内仍未获取到锁,则获取失败long end = System.currentTimeMillis() - start;if (end>=timeout) {return false;}}*//*** 解锁** @param* @param* @return*/public static Boolean unlock(RedisTemplate redisTemplate, String key, String value) {try {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);//redis脚本执行//Object result = redisTemplate.execute(redisScript, Collections.singletonList(key), value))Object result = redisTemplate.delete(Collections.singletonList(key));if (SUCCESS.equals(result)) {return true;}} catch (Exception e) {e.printStackTrace();return false;}return false;}}

注意:   

redisTemplate.execute 是redis脚本执行,可能报错, 具体参考: https://blog.csdn.net/cevery/article/details/108303919

测试:我这里自己创建两个 线程测试

@Slf4j
@Component
public class Schedule {@Resourceprivate RedisTemplate redisTemplate;@Scheduled(cron = "0 0/1 * * * ? ")public void getDeviceStatus() throws InterruptedException {//启动两个线程,测试哪一个能够悠闲抢到Redis锁Test1Runnable test1 = new Test1Runnable();new Thread(test1).start();Test2Runnable test2 = new Test2Runnable();new Thread(test2).start();}//自定义个唯一的Key值public String key = "ZX123456789";//保存数据方法public boolean testSave(){//获取锁boolean isOK = RedisLock.tryLock(redisTemplate, key, "key+1", 2000);//处理业务 ,然后释放锁if(isOK){System.out.println("处理完业务,释放锁==="+RedisLock.unlock(redisTemplate, key, "key+1"));}return isOK;}class Test1Runnable implements Runnable {@Overridepublic void run() {boolean result = testSave();log.info(Thread.currentThread().getName()+"Test1获取锁:"+result);}}class Test2Runnable implements Runnable {@Overridepublic void run() {boolean result = testSave();log.info(Thread.currentThread().getName()+"Test2获取锁:"+result);}}
}

效果:


http://chatgpt.dhexx.cn/article/8kW2nFRA.shtml

相关文章

mysql 高并发写入锁表_使用mysql中的锁解决高并发问题

阿里云产品通用代金券,最高可领1888分享一波阿里云红包. 阿里云的购买入口 为什么要加锁 多核计算机的出现,计算机实现真正并行计算,可以在同一时刻,执行多个任务。在多线程编程中,因为线程执行顺序不可控导致的数据错误。比如,多线程的理想状态是这样的 多线程理想.jpg 但是…

php如何解决高并发问题

如何用PHP解决高并发问题&#xff1f;&#xff08;附源码&#xff09;-php教程-PHP中文网上篇文章给大家介绍了《让我们再进一步了解PHP流程控制语句之if语句吧&#xff01;&#xff01;&#xff01;(附源码)​》&#xff0c;本文继续给大家介绍PHP解决高并发问题https://www.p…

Mysql如何利用乐观锁解决高并发问题

Mysql如何利用乐观锁解决高并发问题 msql Mysql如何利用乐观锁解决高并发问题前言一、案例说明&#xff1a;二、乐观锁&#xff1a;1.介绍:使用版本号实现乐观锁 2.代码实现 总结 前言 例如&#xff1a;在这之前已经许久未写博客了&#xff0c;最近突发奇想还是决定把这个捡起…

Redis解决高并发问题

1 模拟商品抢购和并发的效果 这里模拟一个商品抢购的过程所带来的问题&#xff0c;以及解决问题的思路。 这里模拟的商品抢购过程是一个商品正常购买的过程&#xff0c;其中包含了两个主要的步骤&#xff1a;商品库存减少和商品购买记录的添加。 下面搭建项目环境。 1.1 数…

一文教你如何处理高并发

目录 前言 一、为什么要解决高并发问题 二、性能评估 计算峰值流量方法 本章结论 三、性能测试 测试目的 找到系统最高承受压力的临界点 找出系统中的短板 测试工具 简单测试 1.数据抓包 2.加压测试 3.硬件跟踪 4.JVM跟踪 5.其它组件测试 6.总括 全链路测试&…

高并发场景设计与解决方案

所有的平台或系统建设和维护中&#xff0c;高并发场景都存在&#xff0c;解决方案也是各种样式&#xff0c;本次将从初中、高二个场景给出设计方案。 本文内容&#xff1a;高并发场景定义&#xff0c;高并发初中级场景与解决方案&#xff0c;高并发高级场景与解决方案 第一部分…

数据库关系代数运算

转载&#xff1a;https://wenku.baidu.com/view/f301bf48e45c3b3567ec8b75.html

数据库关系模型与关系运算---2022.2.13

关于外模式&#xff0c;模式&#xff0c;内模式的理解 可以看到用不同的语句进行表示&#xff1a; 关系的性质 概念模式/内模式映射是物理独立性的关键&#xff1b; 外模式/概念模式映射就是逻辑独立性的关键 候选键 (最小组成的超键) 关系中的一个属性组&#xff0c;其值…

关系运算

关系代数是一种抽象的查询语言&#xff0c;它用对关系的运算来表达查询。关系运算的运算对象是关系&#xff0c;运算结果亦是关系&#xff0c;关系代数的运算符包括两类&#xff1a;传统的集合运算和专门的关系运算两类。 传统的集合运算是从关系的水平方向&#xff0c;即行的角…

数据库之间的关系

数据库的设计 1.多表之间的关系 1.一对一&#xff1a;如 人和身份证 &#xff0c;一个人只能一张身份证&#xff0c;一个身份证只能对应一个人 2.一对多&#xff1a;如 部门和员工 一个部门有多个员工&#xff0c;一个员工只能对应一个部门 3.多对多&#xff1a…

数据库(笔记)——关系代数以及相关运算

关系代数 关系代数及其运算符集合运算符关系运算符 总结 关系代数及其运算符 关系代数是一种抽象的查询语言&#xff0c;通过关系的运算来表达查询 关系代数常使用的运算符由如下几类 集合运算符&#xff1a;∪&#xff08;并&#xff09;、∩&#xff08;交&#xff09;、-&…

数据库关系代数详解

文章目录 数据库关系代数1. 传统的关系运算2. 专门的关系运算2.1 关系运算中的基础概念2.2 元组的连接2.3 象集(除法运算重要工具) 3 数学上的运算3.1 并运算3.2 差运算3.3 交运算3.4 笛卡尔积&#xff08;万能运算&#xff09; 4. 关系运算4.1 表格简介4.2 选择&#xff08;Se…

数据库专门的关系运算

本文章用表 选择运算&#xff08;从行的角度运算&#xff09; 选择又称为限制&#xff0c;选择运算符的含义&#xff1a; 在关系R中选择满足给定条件的诸元组 投影&#xff08;从列的角度运算&#xff09; 投影运算符的含义&#xff1a;从表中选出若干属性列组成新的关系 注…

数据库关系代数运算之连接

联接有三种&#xff1a;θ联接和自然联接&#xff08;这里是算术比较符&#xff09;&#xff0c;外联接。 &#xff08;1&#xff09; θ联接 (从R和S的笛卡儿乘积中选取满足条件“iθj”的元组 •&#xff08;2&#xff09;自然联接&#xff08;naturaljoin&#xff09; 两个…

数据库关系代数中除运算讲解和SQL语句的实现

【数据库原理】关系代数篇——除法讲解 陈宇超 编辑总结: 除法运算的一般形式示意图 如何计算RS呢&#xff0c;首先我们引进”象集”的概念&#xff0c;具体意义看下面的陈述即可理解 关系R和关系S拥有共同的属性B、C , RS得到的属性值就是关系R包含而关系S不包含的属性&am…

关系代数基本运算 数据库

操作目录 关系代数的八种基本运算并交差笛卡尔积选择投影连接除总结 关系代数的八种基本运算 并 并&#xff0c;就是将两个或多个表并连起来&#xff0c;需要注意的就是在并的过程中&#xff0c;我们并不是直接一笼统地并起来&#xff0c;而且还要对相同的元祖进行合并&#x…

数据库系统概论----关系运算之除运算

这一周都在复习《数据库系统概论》这门课&#xff0c;看到关系运算的这一节时&#xff0c;对于除运算不是很理解。 通过百度&#xff0c;我觉得也没有得到比较容易理解的讲解。 这里呢&#xff0c;我就分享一下我的理解吧&#xff0c;如有差错的地方&#xff0c;还希望看到这…

数据库-----关系运算

关系数据库概述 相关术语 ◎在现实世界中&#xff0c;描述一个事物常常要抽取其若干特征来表示&#xff0c;这些特征称为属性&#xff0c;如用学号、性别、班级等来描述学生。每个属性的取值范围对应一个值的集合&#xff0c;称为属性的域&#xff0c;如性别的域是{男&#x…

数据库基础--关系代数中的除法运算

除法运算的定义&#xff1a; 这个概念的描述的非常抽象&#xff0c;刚开始学习的同学完全不知所云。这里通过一个实例来说明除法运算的求解过程 设有关系R、S 如图所示&#xff0c;求RS 的结果 求解步骤过程&#xff1a; 第一步&#xff1a;找出关系R和关系S中相同的属…

数据库的运算

数据库的运算可分为集合运算和关系运算。 一、集合运算 • 从关系的水平方向迚行&#xff1b; • 包括&#xff0c;幵、交、差、笛卡尔积运算。 • 幵运算&#xff08;R U S&#xff09;&#xff1a;可实现数据的揑入。 • 差运算&#xff08;R–S&#xff09;&#xff1a;主…