赞
踩
- //1 获取锁,setnx
- //得到一个 uuid 值,作为锁的值
- String uuid = UUID.randomUUID().toString();
-
-
- Boolean lock =
- redisTemplate.opsForValue().setIfAbsent("lock", uuid, 3, TimeUnit.SECONDS);
- //2 获取锁成功
- if (lock) {
-
- //准备删除锁脚本
- //String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
- //DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
- //redisScript.setScriptText(script);
- //redisScript.setResultType(Long.class);
-
- //写自己的业务-就可以有多个操作了
- Long decrement = redisTemplate.opsForValue().decrement("seckillGoods:" + goodsId);
- if (decrement < 0) {//说明这个商品已经没有库存
- //说明当前秒杀的商品,已经没有库存
- entryStockMap.put(goodsId, true);
- //恢复库存为0
- redisTemplate.opsForValue().increment("seckillGoods:" + goodsId);
- //释放锁.-lua为什么使用redis+lua脚本释放锁前面讲过
- redisTemplate.execute(script, Arrays.asList("lock"), uuid);
- model.addAttribute("errmsg", RespBeanEnum.ENTRY_STOCK.getMessage());
- return "secKillFail";//错误页面
- }
-
- //释放分布式锁
- redisTemplate.execute(script, Arrays.asList("lock"), uuid);
-
- } else {
- //3 获取锁失败,返回个信息[本次抢购失败,请再次抢购...]
- model.addAttribute("errmsg", RespBeanEnum.SEC_KILL_RETRY.getMessage());
- return "secKillFail";//错误页面
- }

- if redis.call('get', KEYS[1]) == ARGV[1] then
- return redis.call('del', KEYS[1])
- else return 0
- end
- package com.hspedu.seckill.config;
-
- import org.springframework.context.annotation.Bean;
- import org.springframework.context.annotation.Configuration;
- import org.springframework.core.io.ClassPathResource;
- import org.springframework.data.redis.connection.RedisConnectionFactory;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.script.DefaultRedisScript;
- import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
- import org.springframework.data.redis.serializer.StringRedisSerializer;
-
- //把session信息提取出来存到redis中
- //主要实现序列化, 这里是以常规操作
- @Configuration
- public class RedisConfig {
-
- //自定义 RedisTemplate对象, 注入到容器
- //后面我们操作Redis时,就使用自定义的 RedisTemplate对象
- @Bean
- public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
- RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
- //设置相应key的序列化
- redisTemplate.setKeySerializer(new StringRedisSerializer());
- //value序列化
- //redis默认是jdk的序列化是二进制,这里使用的是通用的json数据,不用传具体的序列化的对象
- redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
- //设置相应的hash序列化
- redisTemplate.setHashKeySerializer(new StringRedisSerializer());
- redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
- //注入连接工厂
- redisTemplate.setConnectionFactory(redisConnectionFactory);
- System.out.println("测试--> redisTemplate" + redisTemplate.hashCode());
- return redisTemplate;
- }
-
- //增加执行脚本
- @Bean
- public DefaultRedisScript<Long> script() {
-
- DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
- //设置要执行的lua脚本位置, 把lock.lua文件放在resources
- redisScript.setLocation(new ClassPathResource("lock.lua"));
- redisScript.setResultType(Long.class);
- return redisScript;
- }
- }
-

@Resource private RedisScript script;
使用lua脚本的目的,是为了读取到当前程序中的锁,和redis中的锁进行对比。
避免A用户因为锁生效时间超时以后自动删除了A当前用户拿到的锁,进而在操作完成时,主动去删除锁时此时有个B用户正好生成了一把锁,那么A用户删除的是B用户的锁,造成数据不一致。
当前程序的锁就是A用户的锁,同时redis存放的也是A用户的锁,就不会造成锁的误删,从而保证了原子性。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。