首页 > 数据库 >Java通过redis实线多线程多用户操作时添加锁

Java通过redis实线多线程多用户操作时添加锁

时间:2024-08-03 19:18:19浏览次数:14  
标签:Java opsForValue redis value 线程 key 多线程 public redisTemplate

背景

由于项目中多出涉及同步数据,同步过程就是从设备上查询数据,将数据库中该设备数据删除,将新数据导入到数据库;多次同步数据或多用户操作,会导致数据库出现重复数据,例如,两个线程同时删除设备数据,同时导入数据,就会出现双倍数据;还有线程1正在导入数据,中途线程2将线程1导入数据之前删除,那么线程1后面导入的数据也会和线程2重复,会出现部分重复;故此,必须对操作数据库添加锁;

实现方式

思路:向redis中添加key-value,当存在key-value则为有锁,反之则为没锁

Spring 封装了 RedisTemplate 对象来进行对redis的各种操作,它支持所有的 redis 原生的 api。 查阅点资料下面总结看下Redis中opsForValue()方法的使用介绍:

要使用 RedisTemplate,必须要先引入它,下面是它的「maven依赖」:

 <!--redis-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <!--<version>2.1.4.RELEASE</version>-->
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>
<!-- fastjson -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.68</version>
</dependency>

引入依赖后,我们需要配置 RedisTemplate。比如:Redis 序列化方式配置:

@Configuration public class RedisConfig { 
 // 设置Redis序列化方式,默认使用的JDKSerializer的序列化方式,效率低,这里我们使用 FastJsonRedisSerializer
    @Bean 
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); // key序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer()); // value序列化
        redisTemplate.setValueSerializer(new FastJsonRedisSerializer<>(Object.class)); // Hash key序列化
        redisTemplate.setHashKeySerializer(new StringRedisSerializer()); // Hash value序列化
        redisTemplate.setHashValueSerializer(new StringRedisSerializer());
        redisTemplate.setConnectionFactory(redisConnectionFactory); return redisTemplate;
    }
}

设置当前的 key 以及 value 值:

redisTemplate.opsForValue().set(key, value)
redisTemplate.opsForValue().set("num","123");

设置当前的 key 以及 value 值并且设置过期时间:

redisTemplate.opsForValue().set(key, value, timeout, unit)
redisTemplate.opsForValue().set("num","123",10, TimeUnit.SECONDS);

//TimeUnit.DAYS //天  
//TimeUnit.HOURS //小时  
//TimeUnit.MINUTES //分钟  
//TimeUnit.SECONDS //秒  
//TimeUnit.MILLISECONDS //毫秒

将旧的 key 设置为 value,并且返回旧的 key(设置 key 的字符串 value 并返回其旧值):

redisTemplate.opsForValue().getAndSet(key, value);

在原有的值基础上新增字符串到末尾:

redisTemplate.opsForValue().append(key, value)

获取字符串的长度:

redisTemplate.opsForValue().size(key)

重新设置 key 对应的值,如果存在返回 false,否则返回 true:

redisTemplate.opsForValue().setIfAbsent(key, value)

设置 map 集合到 redis:

Map valueMap = new HashMap();  
valueMap.put("valueMap1","map1");  
valueMap.put("valueMap2","map2");  
valueMap.put("valueMap3","map3");  
redisTemplate.opsForValue().multiSet(valueMap); 

如果对应的 map 集合名称不存在,则添加否则不做修改:

Map valueMap = new HashMap();  
valueMap.put("valueMap1","map1");  
valueMap.put("valueMap2","map2");  
valueMap.put("valueMap3","map3");  
redisTemplate.opsForValue().multiSetIfAbsent(valueMap); 

通过 increment(K key, long delta) 方法以增量方式存储 long 值(正值则自增,负值则自减):

redisTemplate.opsForValue().increment(key, increment);

批量获取值:(返回传入 key 所存储的值的类型)

public List<String> multiGet(Collection<String> keys) {
    return redisTemplate.opsForValue().multiGet(keys);
}

修改 redis 中 key 的名称:

public void renameKey(String oldKey, String newKey) {
    redisTemplate.rename(oldKey, newKey);
}

如果旧值 key 存在时,将旧值改为新值:

public Boolean renameOldKeyIfAbsent(String oldKey, String newKey) { 
 return redisTemplate.renameIfAbsent(oldKey, newKey);
}

判断是否有 key 所对应的值,有则返回 true,没有则返回 false:

redisTemplate.hasKey(key)

删除单个 key 值:

redisTemplate.delete(key)

批量删除 key:

redisTemplate.delete(keys) //其中keys:Collection<K> keys

设置过期时间:

public Boolean expire(String key, long timeout, TimeUnit unit){
 return redisTemplate.expire(key, timeout, unit);
}

public Boolean expireAt(String key, Date date) { 
 return redisTemplate.expireAt(key, date);
}

返回当前 key 所对应的剩余过期时间:

redisTemplate.getExpire(key);

返回剩余过期时间并且指定时间单位:

public Long getExpire(String key, TimeUnit unit) {
    return redisTemplate.getExpire(key, unit);
}

查找匹配的 key 值,返回一个 Set 集合类型:

public Set<String> getPatternKey(String pattern) { 
    return redisTemplate.keys(pattern);
}

将 key 持久化保存:

public Boolean persistKey(String key) {
    return redisTemplate.persist(key);
}

将当前数据库的 key 移动到指定 redis 中数据库当中:

public Boolean moveToDbIndex(String key, int dbIndex) {
    return redisTemplate.move(key, dbIndex);
}

这里用到的锁比较简单,因此只需要判断key是否存在即可,不过要求项目中不可出现同样的key,否则会出错,例如:3-1-a,因此添加锁用到的命令为:

添加key:

redisTemplate.opsForValue().set(key, value)

判断key是否存在:

redisTemplate.hasKey(key)

删除key:

redisTemplate.delete(key)

较为复杂的功能可以通过给key设置不同的value实现,像我后续的一个功能要求实现线程池指定线程的开启和关闭,显然,Java的线程具有原子性,无法互相干扰,那么就可以通过给key赋值当前时间,从而在需要的时候判断value,实现线程之间的影响
参考:从入门到精通,超强 RedisTemplate 方法详解!

标签:Java,opsForValue,redis,value,线程,key,多线程,public,redisTemplate
From: https://www.cnblogs.com/beijie/p/18336818

相关文章

  • Java使用多线程池给List赋值导致List存在空的处理
    错误示例:publicList<String>test()throwsNuMaxCloudCommonException{ExecutorServiceexecutorService=Executors.newFixedThreadPool(3);List<String>list=newArrayList<>();for(inti=0;i<3;i++){......
  • 计算机Java项目|基于SpringBoot的科研工作量管理系统
    作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验,被多个学校常年聘为校外企业导师,指导学生毕业设计并参与学生毕业答辩指导,有较为丰富的相关经验。期待与各位高校教师、企......
  • 计算机Java项目|基于SpringBoot的纺织品企业财务管理系统设计与实现
    作者简介:Java领域优质创作者、CSDN博客专家、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验,被多个学校常年聘为校外企业导师,指导学生毕业设计并参与学生毕业答辩指导,有较为丰富的相关经验。期待与各位高校教师、企......
  • Java中实现文件上传
    目录1、文件上传本地1.1原理1.2如何使用文件上传1.2.1引入文件上传的依赖 1.2.2配置文件上传拦截器 1.2.3完成文件上传的代码2、文件上传oss服务器 2.1为什么需要上传到oss服务器2.2如何使用oss 2.2.1开启oss服务 2.2.2在Java中引入依赖2.2.3查看......
  • Java中跳转语句的学习
    跳转语句目录跳转语句break语句continue语句break语句break语句可用于上一节介绍的while、repeat-while和for循环结构,它的作用是强行退出循环体,不再执行循环体中剩余的语句。在循环体中使用break语句有两种方式:带有标签和不带标签。语法格式如下:break;//不带标签......
  • JavaScript (十七)——JavaScript 声明提升和严格模式
    目录JavaScript声明提升JavaScript初始化不会提升在头部声明你的变量JavaScript严格模式(usestrict)使用"usestrict"指令严格模式声明严格模式的限制JavaScript声明提升JavaScript中,函数及变量的声明都将被提升到函数的最顶部。JavaScript中,变量可以在......
  • JavaScript(十八)——JavaScript 使用误区
    目录赋值运算符应用错误比较运算符常见错误加法与连接注意事项浮点型数据使用注意事项JavaScript字符串分行错误的使用分号语句使用注意事项return使用注意事项数组中使用名字来索引定义数组元素,最后不能添加逗号定义对象,最后不能添加逗号Undefined不是Null程......
  • 【Java】如何定位线上的OOM
    1.概述本章我们主要讲解如何定位线上的OOM的问题。2.OOM原因OOM的原因有很多种,下面举几个列子。2.1一次申请对象过多比如你查询数据库数据,几千万的数据一次查询完毕,然后都放在内存,然后当然会导致OOM了。解决:可以分页查询2.2内存耗尽-未释放比如我们使用数据库连......
  • Java - Stream流
    Stream流的使用结合Lambda表达式简化集合、数组操作获取Stream流对象->使用中间方法处理数据->使用终结方法处理数据调用方式中间方法1.中间方法返回的是新的Stream流,故每个Stream流只能使用一次中间方法,一般使用链式编程2.修改Stream流中数据不会影响原集合或数组......
  • Java中的不同数据类型的方法调用
    数组在Java中,数组是一个基础的数据结构,用来存储固定大小的同类型元素。数组本身在Java中是一个对象,但它的方法比较有限,主要依赖于Java的Arrays类来进行数组操作。排序sort():对整个数组或指定范围的元素进行排序。重载版本支持所有基本类型数组和对象数组。对于对象数组......