1.pom文件添加redis支持

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

2.application.properties或者(application.yml)添加redis配置

spring.redis.database=1
spring.redis.host=172.xx.xx.xx
spring.redis.password=123456
spring.redis.port=6379

上面的spring.redis.host替换成自己的redis服务地址,如果没有用到密码则删除spring.redis.password配置即可

3.redis工具类

package com.example.demo;

import com.example.demo.extend.FastJsonRedisSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component; import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.List; @Component
public class RedisUtil { @Autowired
private RedisTemplate<String,String> template; @PostConstruct
public void init(){
template.setKeySerializer(template.getStringSerializer());
template.setValueSerializer(template.getStringSerializer());
template.setHashKeySerializer(template.getStringSerializer());
template.setHashValueSerializer(template.getStringSerializer());
} /**
* 通过lua脚本 加锁并设置过期时间
* @param key 锁key值
* @param value 锁value值
* @param expire 过期时间,单位秒
* @return true:加锁成功,false:加锁失败
*/
public boolean getLock(String key,String value,String expire){
DefaultRedisScript<String> redisScript = new DefaultRedisScript<String>();
redisScript.setResultType(String.class);
String strScript = "";
strScript +=" if redis.call('setNx',KEYS[1],ARGV[1])==1 then ";
strScript +=" return redis.call('expire',KEYS[1],ARGV[2]) ";
strScript +=" else";
strScript +=" return 0 ";
strScript +=" end ";
redisScript.setScriptText(strScript);
try{
Object result = this.template.execute(redisScript,template.getStringSerializer(),template.getStringSerializer(), Collections.singletonList(key),value,expire);
System.out.println("redis返回:"+result);
return "1".equals(""+result);
}catch (Exception e){
//可以自己做异常处理
return false;
} } /**
* 通过lua脚本释放锁
* @param key 锁key值
* @param value 锁value值(仅当redis里面的value值和传入的相同时才释放,避免释放其他线程的锁)
* @return true:释放锁成功,false:释放锁失败(可能已过期或者已被释放)
*/
public boolean releaseLock(String key,String value){
DefaultRedisScript<String> redisScript = new DefaultRedisScript<>();
redisScript.setResultType(String.class);
String strScript = "";
strScript +="if redis.call('get',KEYS[1]) == ARGV[1] then ";
strScript +=" return redis.call('del',KEYS[1]) ";
strScript +="else ";
strScript +=" return 0 ";
strScript +="end ";
redisScript.setScriptText(strScript);
try{
Object result = this.template.execute(redisScript,template.getStringSerializer(),template.getStringSerializer(), Collections.singletonList(key),value);
return "1".equals(""+result);
}catch (Exception e){
//可以自己做异常处理
return false;
}
}
}

redis锁用到的是setNx命令,这个命令的意思是如果redis里面存在这个key则不再添加,如果key不存在则添加成功,当setNx设置成功之后再给这个值设置一个超期时间来防止出现极端情况(断网,服务终止)导致锁没有被及时释放的情况。

上面的加锁和解锁都是通过lua脚本进行,redis里面lua脚本执行时是原子操作,可以保证加锁和设置超时同时成功或者失败,不会出现设置值成功 添加超时时间失败的情况

4.使用

在需要加锁的地方注入RedisUtil对象即可。有问题的可以留言一起探讨细节问题

@Autowired
private RedisLockUtil redisUtil;
boolean lock = this.redisUtil.getLock("FORM_SUBMIT"+formId,formId,"2");
if(!lock){
//未获得锁
throw new ServiceException("当前已经有任务在执行!");
} //---------执行业务逻辑 if(lock){
//释放锁不关心成功与否
this.redisUtil.releaseLock("FORM_SUBMIT"+formId,formId);
}

上面的业务逻辑最好放在try catch中执行,释放锁的代码放到finally里面执行。

5.考虑各种情况下会不会导致bug的出现

5.1:加锁失败

  拿不到锁业务逻辑不执行,没问题

5.2:加锁成功,释放锁失败

  网络原因或者其他原因没有释放掉,没关系 ,超时时间过了就会自己释放,没问题

5.3:加锁成功,业务执行时间过长,锁已经被redis自己释放

  此种情况需要根据业务的实际情况设置合理的超时时间,可能会出问题,原因在于 基于分布式的系统是无法避免类似的问题,具体可以参考如下博客的文章,

此文章引入了 关于Redis分布式锁的安全性问题,在分布式系统专家Martin Kleppmann和Redis的作者antirez之间发生过的一场争论,内容很精彩。https://blog.csdn.net/paincupid/article/details/75094550

springboot利用redis实现分布式锁(redis为单机模式)的更多相关文章

  1. Redis除了做缓存--Redis做消息队列/Redis做分布式锁/Redis做接口限流

    1.用Redis实现消息队列 用命令lpush入队,rpop出队 Long size = jedis.lpush("QueueName", message);//返回存放的数据条数 ...

  2. Redis 中的原子操作(3)-使用Redis实现分布式锁

    Redis 中的分布式锁如何使用 分布式锁的使用场景 使用 Redis 来实现分布式锁 使用 set key value px milliseconds nx 实现 SETNX+Lua 实现 使用 R ...

  3. Java基于redis实现分布式锁(SpringBoot)

    前言 分布式锁,其实原理是就是多台机器,去争抢一个资源,谁争抢成功,那么谁就持有了这把锁,然后去执行后续的业务逻辑,执行完毕后,把锁释放掉. 可以通过多种途径实现分布式锁,例如利用数据库(mysql等 ...

  4. 利用多写Redis实现分布式锁原理与实现分析(转)

    利用多写Redis实现分布式锁原理与实现分析   一.关于分布式锁 关于分布式锁,可能绝大部分人都会或多或少涉及到. 我举二个例子:场景一:从前端界面发起一笔支付请求,如果前端没有做防重处理,那么可能 ...

  5. 利用redis实现分布式锁知识点总结及相关改进

    利用redis实现分布式锁知识点总结及相关改进 先上原文,本文只为总结及对相关内容的质疑并提出若干意见,原文内容更详细https://www.cnblogs.com/linjiqin/p/800383 ...

  6. 利用redis实现分布式锁

    分布式锁一般有三种实现方式: 1. 数据库乐观锁: 2. 基于ZooKeeper的分布式锁: 3. 基于Redis的分布式锁: 这里大概说一下三种方式的优缺点,数据库乐观锁优点是实现简单,只需要for ...

  7. spring boot 利用redisson实现redis的分布式锁

    原文:http://liaoke0123.iteye.com/blog/2375469 利用redis实现分布式锁,网上搜索的大部分是使用java jedis实现的. redis官方推荐的分布式锁实现 ...

  8. SpringBoot电商项目实战 — Redis实现分布式锁

    最近有小伙伴发消息说,在Springboot系列文第二篇,zookeeper是不是漏掉了?关于这个问题,其实我在写第二篇的时候已经考虑过,但基于本次系列文章是实战练习,在项目里你能看到Zookeepe ...

  9. 通过Redis 实现分布式锁_利用Jedis 客户端

    前言 分布式锁一般有三种实现方式: 数据库乐观锁:2. 基于Redis的分布式锁:3. 基于ZooKeeper的分布式锁. 本篇博客将介绍第二种方式,基于Redis实现分布式锁. 虽然网上已经有各种介 ...

  10. SpringBoot集成Redis 一 分布式锁 与 缓存

    1.添加依赖及配置(application.yml) <!-- 引入redis依赖 --> <dependency> <groupId>org.springfram ...

随机推荐

  1. 29.2 Iterator 迭代器ConcurrentModificationException:并发修改异常处理

    /** Iterator:迭代器* * 需求:判断集合中是否包含元素java,如果有则添加元素android * Exception in thread "main" java.u ...

  2. slice使用了解

    切片 什么是slice slice的创建使用 slice使用的一点规范 slice和数组的区别 slice的append是如何发生的 复制Slice和Map注意事项 什么是slice Go中的切片,是 ...

  3. Docker-Bridge Network 01 容器间通信

    本小节介绍bridge network模式下,单机上的容器网络拓扑及通信. 1.前言 对于单机上的容器,Docker提供了bridge.host.none三种网络.我们首先介绍经典的bridge模式. ...

  4. 教你如何安装和使用Python pip

    前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:Starshot PS:如有需要Python学习资料的小伙伴可以加点击 ...

  5. work of 1/5/2016

    part 组员                今日工作              工作耗时/h 明日计划 工作耗时/h    UI 冯晓云 UI页面切换,词本显示下滑条     6 继续下滑条等增删补 ...

  6. niuke --abc

    链接:https://ac.nowcoder.com/acm/contest/1083/A来源:牛客网 给出一个字符串s,你需要做的是统计s中子串”abc”的个数.子串的定义就是存在任意下标a< ...

  7. 排序算法代码实现-Java

    前言 为了准备面试,从2月开始将排序算法认认真真得刷了一遍,通过看书看视频,实践打代码,还有一部分的leetcode题,自己感觉也有点进步,将笔记记录总结发出来. 冒泡排序 该排序就是一种像泡泡浮到水 ...

  8. react-i8n

    1. 在项目中安装 npm install react-intl --save 2.兼容Safari各个版本需要安装intl npm install intl --save 3.编写语言包 1.)新建 ...

  9. idea中哪些好用到飞起的插件,偷懒神器

    idea中开发人员的偷懒神器-插件  本期推荐一些开发人员常用的一些idea插件.偷懒神器在此,不再秃头! 1. idea安装插件的方法.  file->setting->plugins ...

  10. thinkphp5 csv格式导入导出(多数据处理)

    关于csv文件格式的导出导入个人见解 先上代码: <?php namespace think; class Csv { /** * 导出csv文件 * @param $list 数据源 * @p ...