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. Linux C++ 网络编程学习系列(3)——多路IO之poll实现

    poll实现多路IO 源码地址:https://github.com/whuwzp/linuxc/tree/master/poll 源码说明: server.cpp: 监听127.1:6666,功能是 ...

  2. 数据表记录包含表索引和数值(int范围的整数),请对表索引相同的记录进行合并,即将相同索引的数值进行求和运算,输出按照key值升序进行输出

    此题如果直接使用有序的TreeMap就不需要这样折腾: 1.map的key值唯一性,故就不在需要set集合来去重 2.使用map后利用key的唯一性,把序列号相同的数据直接加在一起,代码会很简洁 pa ...

  3. lua使用笔记2:Linux 中安装php的lua扩展

    安装lua扩展的前提是lua已经安装好,如果没有安装,参照 1.http://pecl.php.net/package/lua 下载lua扩展 或者Linux下直接输入 wget http://pec ...

  4. 深度学习之文本分类模型-前馈神经网络(Feed-Forward Neural Networks)

    目录 DAN(Deep Average Network) Fasttext fasttext文本分类 fasttext的n-gram模型 Doc2vec DAN(Deep Average Networ ...

  5. Xss Game挑战

    前言 最新学习了下xss的更深入的东西,学习了一波浏览器解析机制和XSS向量编码的知识. 这里就些xss的练习题巩固知识 学习的话结合如下两篇文章看,从例子和基础原理层面都有: http://boba ...

  6. 2020-MRCTF

    ez_bypass I put something in F12 for you include 'flag.php'; $flag='MRCTF{xxxxxxxxxxxxxxxxxxxxxxxxx} ...

  7. 学习笔记-CTF密码相关

    RSA共模攻击 RSA基本原理 ①  选择两个大的质数p和q,N=pq: ②  根据欧拉函数,求得r=(p-1)(q-1): ③  选一个小于r的整数e,求得e关于模r的模反元素d: ④  将p和q的 ...

  8. 2019-2020-1 20199326《Linux内核原理与分析》第五周作业

    第五周学习内容 庖丁解牛Linux内核分析第四章:系统调用的三层机制(上) Linux内核分析实验四 学到的一些知识 4.1用户态.内核态.中断 宏观上Linux操作系统的体系架构分为用户态和内核态 ...

  9. 2019-2020-1 20199326《Linux内核原理与分析》第二周作业

    本周总结:本周的学习内容主要是庖丁解牛Linux的第一章,然后看完书后,又跟着云班课加深学习了一下第一章的内容.第一章主要讲述了linux里的汇编指令的一些指令,比如movl,pushl,popl等等 ...

  10. Linux系统管理第二次作业 目录和文件管理 rpm安装 创建yum仓库

    chapter02 - 03 作业      1.分别用cat \tac\nl三个命令查看文件/etc/ssh/sshd_config文件中的内容,并用自己的话总计出这三个文档操作命令的不同之处? [ ...