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. nginx部署静态网站

    实验环境 服务器:centos7.5 1核1G Nginx版本:nginx-1.14.2 主题 部署静态文件 根据不同url请求路径,定向到不同的系统文件夹 部署静态文件 假设nginx安装在“/us ...

  2. 数据库连接(1)-从JDBC到MyBatis

    摘要 因为有持久层框架,和Spring的存在,越来越多的人对数据库连接这块不甚了解,只知使用方便,不知其原理.所以写一个数据库连接的系列文章,总结下本人在数据库连接方面遇到的问题,和对数据库连接的理解 ...

  3. Python就业指导

    一年一度的金三银四招聘旺季又要到了,最近有很多同学希望我能给他们一些关于python的就业指导:之前出过一期关于java的就业指导,但是并不是很完善,所以希望这期关于python的就业指导能够很全面很 ...

  4. Dalvik 虚拟机操作码

    Dalvik 虚拟机操作码 表中的vx.vy.vz表示某个Dalvik寄存器.根据不同指令可以访问16.256或64K寄存器. 表中lit4.lit8.lit16.lit32.lit64表示字面值(直 ...

  5. KVO讲解

    最近一直在写swift项目,没有时间更新自己的技术博客,以前在博客里面写过KVO的底层原理,今天我们来看一下KVO的整个使用过程和使用场景(附有demo),大约花大家10-15分钟时间,希望大家看完博 ...

  6. java爬虫系列第三讲-获取页面中绝对路径的各种方法

    在使用webmgiac的过程中,很多时候我们需要抓取连接的绝对路径,总结了几种方法,示例代码放在最后. 以和讯网的一个页面为例: xpath方式获取 log.info("{}", ...

  7. 零基础学Python--------第10章 文件及目录操作

    第10章 文件及目录操作 10.1 基本文件操作 在Python中,内置了文件(File)对象.在使用文件对象时,首先需要通过内置的open() 方法创建一个文件对象,然后通过对象提供的方法进行一些基 ...

  8. MySQL数据库中的四种隔离级别

    事务的隔离性比想象的要复杂,在 SQL 标准中定义了四种级别的隔离级别.通常而言,较低级别的隔离通常可以执行更高的并发,系统的开销也更低 READ UNCOMMITTED 该级别为未提交读.在该级别中 ...

  9. EF时,数据库字段和实体类不一致问题

    场景:由于一些原因,实体中属性比数据库中字段多了一个startPage属性.PS:controllers中用实体类去接收参数,但是传入的参数比数据库中实体表多了一个字段, 这种情况下,应该建一个vie ...

  10. 微信小程序 canvas 文字自动换行

    Page({ drawCanvas: function(ctx) {// 地址 ctx.setFontSize() ctx.setFillStyle('#9E7240') ctx.textAlign= ...