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<