@Component
public class RedisDistributedLockUtils {
private static final String NX = "NX";
private static final String EX = "EX";
private static final String LOCK_OK = "OK";
private static final Long UNLOCK_OK = 1L;
private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
private static ThreadLocal<String> LOCK_VALUE = new ThreadLocal<String>() {
@Override
protected String initialValue() {
return UUID.randomUUID().toString();
}
};
private static RedisTemplate<String, String> redisTemplate;
@Autowired
public void setRedisTemplate(RedisTemplate redisTemplate) {
RedisDistributedLockUtils.redisTemplate = redisTemplate;
}
/**
* @param key
* @param expireSeconds 过期时间
* @return
*/
public static boolean lock(String key, int expireSeconds) {
return redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
Object nativeConnection = connection.getNativeConnection();
if (nativeConnection instanceof JedisCluster) {
JedisCluster jedisCluster = (JedisCluster) nativeConnection;
String result = jedisCluster.set(key, LOCK_VALUE.get(), NX, EX, expireSeconds);
return LOCK_OK.equals(result);
}
if (nativeConnection instanceof Jedis) {
Jedis jedis = (Jedis) nativeConnection;
String result = jedis.set(key, LOCK_VALUE.get(), NX, EX, expireSeconds);
return LOCK_OK.equals(result);
}
return false;
}
});
}
public static boolean unlock(String key) {
return redisTemplate.execute(new RedisCallback<Boolean>() {
@Override
public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
Object nativeConnection = connection.getNativeConnection();
if (nativeConnection instanceof JedisCluster) {
JedisCluster jedisCluster = (JedisCluster) nativeConnection;
Object unlock = jedisCluster.eval(UNLOCK_SCRIPT, Collections.singletonList(key), Collections.singletonList(LOCK_VALUE.get()));
return UNLOCK_OK.equals(unlock);
}
if (nativeConnection instanceof Jedis) {
Jedis jedis = (Jedis) nativeConnection;
Object unlock = jedis.eval(UNLOCK_SCRIPT, Collections.singletonList(key), Collections.singletonList(LOCK_VALUE.get()));
return UNLOCK_OK.equals(unlock);
}
return false;
}
});
}
}
假如:线程A代码执行时间超过分布式锁时间,这时锁可能已被线程B获取,此时A线程去释放锁可能将B线程获取的锁给释放掉,为防止此异常,加入线程本地UUID值,A线程释放锁时,对比value和A线程UUID不相同,说明是其他线程获取的锁,此时不释放,异常成功规避。