1.先配置spring-data-redis

首先是依赖

    <dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>1.8.4.RELEASE</version>
</dependency>

redisconfig 配置类

@Configuration
@PropertySource("classpath:irongbei.properties")
public class RedisConfig extends JCacheConfigurerSupport {
@Autowired
private Environment environment; @Bean
public RedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory fac = new JedisConnectionFactory();
fac.setHostName(environment.getProperty("redis.host"));
fac.setPort(Integer.parseInt(environment.getProperty("redis.port")));
fac.setPassword(environment.getProperty("redis.password"));
fac.setTimeout(Integer.parseInt(environment.getProperty("redis.timeout")));
// fac.getPoolConfig().setMaxIdle(Integer.parseInt(environment.getProperty("redis.maxIdle")));
// fac.getPoolConfig().setMaxTotal(Integer.parseInt(environment.getProperty("redis.maxTotal")));
// fac.getPoolConfig().setMaxWaitMillis(Integer.parseInt(environment.getProperty("redis.maxWaitMillis")));
// fac.getPoolConfig().setMinEvictableIdleTimeMillis(
// Integer.parseInt(environment.getProperty("redis.minEvictableIdleTimeMillis")));
// fac.getPoolConfig()
// .setNumTestsPerEvictionRun(Integer.parseInt(environment.getProperty("redis.numTestsPerEvictionRun")));
// fac.getPoolConfig().setTimeBetweenEvictionRunsMillis(
// Integer.parseInt(environment.getProperty("redis.timeBetweenEvictionRunsMillis")));
// fac.getPoolConfig().setTestOnBorrow(Boolean.parseBoolean(environment.getProperty("redis.testOnBorrow")));
// fac.getPoolConfig().setTestWhileIdle(Boolean.parseBoolean(environment.getProperty("redis.testWhileIdle")));
return fac;
} @Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, String> redis = new RedisTemplate<>();
// 设置redis的String/Value的默认序列化方式
DefaultStrSerializer defaultStrSerializer = new DefaultStrSerializer();
redis.setKeySerializer(defaultStrSerializer);
redis.setValueSerializer(defaultStrSerializer);
redis.setHashKeySerializer(defaultStrSerializer);
redis.setHashValueSerializer(defaultStrSerializer);
redis.setConnectionFactory(redisConnectionFactory);
redis.afterPropertiesSet();
return redis;
}
} class DefaultStrSerializer implements RedisSerializer<Object> {
private final Charset charset; public DefaultStrSerializer() {
this(Charset.forName("UTF8"));
} public DefaultStrSerializer(Charset charset) {
Assert.notNull(charset, "Charset must not be null!");
this.charset = charset;
} @Override
public byte[] serialize(Object o) throws SerializationException {
return o == null ? null : String.valueOf(o).getBytes(charset);
} @Override
public Object deserialize(byte[] bytes) throws SerializationException {
return bytes == null ? null : new String(bytes, charset); }
}

redisLock类 分布式锁实现的类

@Component
public class RedisLock { private static final Logger log = LoggerFactory.getLogger(RedisLock.class); @Autowired
private RedisTemplate<String, String> redisTemplate; /**
*
* @param key
* @param value 当前时间+超时时间
* @return
*/
public boolean lock(String key,String value){ if(redisTemplate.opsForValue().setIfAbsent(key,value)){
log.info(" [Redis分布式锁] key:[{}] 获取到锁",key);
return true;
}
//oldvalue 俩个线程都返回A
String currentValue = redisTemplate.opsForValue().get(key);
//如果锁过期->这里如果一个key的value时间是小于当前当前时间 那就是过期了,如果大于当前时间才没有过期
if(StringUtils.isNotEmpty(currentValue) && Long.parseLong(currentValue) <System.currentTimeMillis()){ //获取上一个锁的时间
//第一个线程获取上一个oldvalue 然后设置一个新的值进去 第二个线程就获取到是新的值.
String oldValue = redisTemplate.opsForValue().getAndSet(key,value);
//3 这一步就只有第一个线程能匹配到了 第二个线程就获取不到了
if(StringUtils.isNotEmpty(oldValue) && oldValue.equals(currentValue)){
log.info(" [Redis分布式锁] key:[{}] 获取到锁",key);
return true;
} } return false; } /**
* 解锁
* @param key
* @param value
*/
public void unlock(String key ,String value){
try {
String currentValue = redisTemplate.opsForValue().get(key);
if(StringUtils.isNotEmpty(currentValue) && currentValue.equals(value)){
redisTemplate.opsForValue().getOperations().delete(key);
}
} catch (Exception e) {
log.error(" [redis分布式锁] 解锁异常, {} ",e);
} }
}

测试类:

@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@ContextConfiguration(locations = {"/spring-context.xml","/spring-context-jedis.xml","/spring-context-shiro.xml"})
public class RedisLockTest {
@Resource(name = "threadPoolTaskExecutor")
private ThreadPoolExecutor taskExecutor; @Autowired
private RedisLock redisLock;
private long timeout = 5*1000; private static final Logger logger =LoggerFactory.getLogger(RedisLockTest.class); @Test
public void test (){
for (int i = 0; i <200 ; i++) {
taskExecutor.execute(()->{
String time = String.valueOf(System.currentTimeMillis()+timeout);
if(!redisLock.lock("testlock",time)){
logger.info("哎呦喂..人太多了 在排队中..");
return;
}else {
try {
Thread.sleep(4000);
redisLock.unlock("testlock",time);
} catch (InterruptedException e) {
e.printStackTrace();
}
} });
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} }
}

补充

和同事讨论,结果确实如同事所说,解锁的时候有些不严谨,因为是分俩步操作的.可能会在del之前被别人获取到锁然后再del删除掉别人获取的锁.下面是新的解锁方式,目前有些公司redis服务器不支持这样的命令

  为什么使用lua语言在操作Redis 主要是保证操作的原子性.一步操作

  但是使用lua要非常小心,如果脚本错误可能会阻塞整个Redis实例

 private static final Long SUCCESS = 1L;
/**
* 释放锁
* @param key
* @param value
* @return
*/
public boolean releaseLock(String key, String value) {
key =prifix+key;
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); try {
Object result = redisTemplate.execute(redisScript, Collections.singletonList(key), value);
System.out.println(result);
if (SUCCESS.equals(result)) {
log.info("[redis 分布式锁解锁成功] key:[{}] 结果:{}",key,result);
return true;
}
log.info("[redis 分布式锁解锁失败] key:[{}] 结果{}",key,result);
} catch (Exception e) {
log.info("[redis 分布式锁解锁失败] key:[{}] msg:{}",key,e.getMessage());
e.printStackTrace();
} return false;
}

 测试代码

    @Test
public void testPrefix(){
redisLock.lock("test","10000");
redisLock.lock("test1","10000");
// redisLock.lock("test2","10000");
// redisLock.lock("test3","10000");
redisLock.releaseLock("test","10000");
redisLock.releaseLock("test1","10002");
}

springMVC 实现redis分布式锁的更多相关文章

  1. Lua脚本在redis分布式锁场景的运用

    目录 锁和分布式锁 锁是什么? 为什么需要锁? Java中的锁 分布式锁 redis 如何实现加锁 锁超时 retry redis 如何释放锁 不该释放的锁 通过Lua脚本实现锁释放 用redis做分 ...

  2. 利用redis分布式锁的功能来实现定时器的分布式

    文章来源于我的 iteye blog http://ak478288.iteye.com/blog/1898190 以前为部门内部开发过一个定时器程序,这个定时器很简单,就是配置quartz,来实现定 ...

  3. Redis分布式锁

    Redis分布式锁 分布式锁是许多环境中非常有用的原语,其中不同的进程必须以相互排斥的方式与共享资源一起运行. 有许多图书馆和博客文章描述了如何使用Redis实现DLM(分布式锁管理器),但是每个库都 ...

  4. redis分布式锁和消息队列

    最近博主在看redis的时候发现了两种redis使用方式,与之前redis作为缓存不同,利用的是redis可设置key的有效时间和redis的BRPOP命令. 分布式锁 由于目前一些编程语言,如PHP ...

  5. redis咋么实现分布式锁,redis分布式锁的实现方式,redis做分布式锁 积极正义的少年

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

  6. spring boot redis分布式锁

    随着现在分布式架构越来越盛行,在很多场景下需要使用到分布式锁.分布式锁的实现有很多种,比如基于数据库. zookeeper 等,本文主要介绍使用 Redis 做分布式锁的方式,并封装成spring b ...

  7. Redis分布式锁的正确实现方式

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

  8. Redis分布式锁---完美实现

    这几天在做项目缓存时候,因为是分布式的所以需要加锁,就用到了Redis锁,正好从网上发现两篇非常棒的文章,来和大家分享一下. 第一篇是简单完美的实现,第二篇是用到的Redisson. Redis分布式 ...

  9. redis分布式锁实践

    分布式锁在多实例部署,分布式系统中经常会使用到,这是因为基于jvm的锁无法满足多实例中锁的需求,本篇将讲下redis如何通过Lua脚本实现分布式锁,不同于网上的redission,完全是手动实现的 我 ...

随机推荐

  1. Sitecore® 8.2 Professional Developer考试心得

    因工作原因入了Sitecore的坑.. 不了解Sitecore认证考试的同学请移步: http://www.cnblogs.com/edisonchou/archive/2018/08/17/9488 ...

  2. new Date()传参的浏览器兼容性

    测试以下是在IE9的测试情况 可以看到IE9不支持new Date('2018-1-2')和new Date('123456'),但是支持new Date('2018-01-02').new Date ...

  3. 我是如何拿到蚂蚁金服 offer 的 ?

    阅读本文大概需要 5.6 分钟. 作者:翟洪毅 一.梦想和被拒 二.积累 三.结语   首先介绍一下投稿作者 翟洪毅,16年华理计算机本科毕业.在年前拿到了蚂蚁金服Java开发的offer,P6. 工 ...

  4. Spring Boot 2.2 增加了一个新功能,启动飞起~

    前几天栈长分享了一个好玩的框架:一个比Spring Boot快44倍的Java框架!,是不是感觉 Spring Boot 略慢?今天讲一下 Spring Boot 添加的这个新特性,可以大大提升 Sp ...

  5. 我的2018OKR年终回顾与2019OKR初步规划

    转眼一年又过去了,在这个年底的时候,按照国际惯例,又到了重新回顾审视一下这一年来的得失,规划一下明年的奋斗方向的时候了.因此,我继续使用OKR来给自己做Review和Planning,也希望自己能够保 ...

  6. 如何在ASP.NET Core中使用JSON Patch

    原文: JSON Patch With ASP.NET Core 作者:.NET Core Tutorials 译文:如何在ASP.NET Core中使用JSON Patch 地址:https://w ...

  7. 《前端之路》之三 数组的属性 && 操作方法(下)

    咱们 接着上篇来讲- 7.slice() 从某个已有的数组返回选定的元素 经常用来将类数组转化成数组,这样做一方面可以利用现有的数组方法更加方便的处理,另一方面是处于性能的考虑 var f = fun ...

  8. 【Android Studio安装部署系列】三、Android Studio项目目录结构

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 概述 简单介绍下Android studio新建项目的目录结构. 常用项目结构类型 在Android Studio中,提供了以下几种项目结 ...

  9. okhttputils【 Android 一个改善的okHttp封装库】使用(三)

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 这一篇主要讲一下将OkHttpUtils运用到mvp模式中. 数据请求地址:http://www.wanandroid.com/to ...

  10. 浅谈RabbitMQ Management

    在上一篇博文(centos安装MQ)中,介绍了如何在linux安装rabbitmq,以及安装维护插件,这篇主要介绍介绍rabbitmq_management的UI. vrabbitmq_managem ...