SpringBoot(二十七)整合Redis之分布式锁

 v阅读目录

String result = template.execute(new RedisCallback<String>() {             @Override             public String doInRedis(RedisConnection connection) throws DataAccessException {                 JedisCommands commands = (JedisCommands) connection.getNativeConnection();                 return commands.set(key, "锁定的资源", "NX", "PX", 3000);             }         });
复制代码

注意: Redis 从2.6.12版本开始 set 命令支持 NX 、 PX 这些参数来达到 setnx 、 setex 、 psetex 命令的效果,文档参见: SET — Redis 命令参考

NX: 表示只有当锁定资源不存在的时候才能 SET 成功。利用 Redis 的原子性,保证了只有第一个请求的线程才能获得锁,而之后的所有线程在锁定资源被释放之前都不能获得锁。

v锁的进阶

模拟一个比较常见的秒杀场景,这时候就需要用到锁。

2.1 创建RedisLockHelper

复制代码
package com.demo.common;  import com.google.common.base.Strings; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component;  /**  * Created by toutou on 2019/1/27.  */ @Component @Slf4j public class RedisLockHelper {     @Autowired     private StringRedisTemplate stringRedisTemplate;      /**      * 加锁      * @param targetId   targetId - 商品的唯一标志      * @param timeStamp  当前时间+超时时间 也就是时间戳      * @return     */    public boolean lock(String targetId,String timeStamp){         if(stringRedisTemplate.opsForValue().setIfAbsent(targetId,timeStamp)){             // 对应setnx命令,可以成功设置,也就是key不存在            return true;         }          // 判断锁超时 - 防止原来的操作异常,没有运行解锁操作  防止死锁        String currentLock = stringRedisTemplate.opsForValue().get(targetId);         // 如果锁过期 currentLock不为空且小于当前时间        if(!Strings.isNullOrEmpty(currentLock) && Long.parseLong(currentLock) < System.currentTimeMillis()){             // 获取上一个锁的时间value 对应getset,如果lock存在            String preLock =stringRedisTemplate.opsForValue().getAndSet(targetId,timeStamp);              // 假设两个线程同时进来这里,因为key被占用了,而且锁过期了。获取的值currentLock=A(get取的旧的值肯定是一样的),两个线程的timeStamp都是B,key都是K.锁时间已经过期了。             // 而这里面的getAndSet一次只会一个执行,也就是一个执行之后,上一个的timeStamp已经变成了B。只有一个线程获取的上一个值会是A,另一个线程拿到的值是B。            if(!Strings.isNullOrEmpty(preLock) && preLock.equals(currentLock) ){                 // preLock不为空且preLock等于currentLock,也就是校验是不是上个对应的商品时间戳,也是防止并发                return true;             }         }         return false;     }       /**      * 解锁      * @param target      * @param timeStamp      */    public void unlock(String target,String timeStamp){         try<
                    
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信