加锁代码很简单,lockName不存在时设值value,一分钟时间过期,redis可以保证原子性
public boolean lock(String lockName, String value) {
return valueOperations.setIfAbsent(lockName, value, Duration.ofSeconds(60));
}
解锁用redis脚本实现原子性,lockName和value作为脚本参数
public boolean unlock(String lockName, String value) {
Long result = redisTemplate.execute(redisScript, Collections.singletonList(lockName), "value");
return 1 == result;
}
get lockName 的值与value比较,相等则删除(解锁),否则直接返回解锁成功
@Bean
public DefaultRedisScript<Long> unlockScript() {
DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
redisScript.setScriptText("if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 1 end");
redisScript.setResultType(Long.class);
return redisScript;
}
测试代码
String lockName = "myLockName";
String lockValue = "myLockValue";
System.out.println("lock result: " + redisLock.lock(lockName, lockValue));
System.out.println("unlock result: " + redisLock.unlock(lockName, lockValue));
lock result: true
unlock result: true
似乎加锁和解锁都成功了,但是redis里锁依然存在,没有被删除
原因在于springboot配置RedisTemplate采用了json序列化:
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
所以加锁时myLockName的值不是myLockValue而是"myLockValue",而解锁时执行的redis脚本的参数是不带双引号的,导致直接走到else逻辑返回1。