背景
之前介绍了一种方式,这里又介绍了一种方式,不过都差不多,都是两步
1.获取锁
2.释放锁
之前介绍的一种方式参考 juejin.cn/post/687980…
原理
实现差不多。
之前是基于redis客户端实现,现在是基于spring RedisTemplate实现,不过都差不多,因为spring RedisTemplate只是一个门面,真正的实现还是基于redis客户端。不过不是jedis,而是其他的客户端。
RedisTemplate只是spring-data-redis.jar里的一个类,基于门面设计模式的一个类,具体实现是各个不同的redis客户端。早期是jedis客户端,现在默认是lettuce。
门面模式
架构图
门面模式就是为了可以切换不同的客户端。
关于门面模式参考 juejin.cn/post/684490…
代码
package xxx.common.redis;
import java.util.Collections;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
public class DistributedLock<T> implements BeanFactoryAware{
private RedisTemplate<String, T> redisTemplate;
private static final Long SUCCESS = 1L;
public static final String BEAN_NAME="distributedLock";
private Log logger = LogFactory.getLog(this.getClass());
/**
* 获取锁
* @param lockKey
* @param value
* @param expireTime:单位-秒
* @return
*/
public boolean getLock(String lockKey, String value, int expireTime){
boolean ret = false;
try{
String script = "if redis.call('set',KEYS[1],ARGV[1],'NX','EX',ARGV[2]) then return 1 else return 0 end";
//指定ReturnType为Long.class,只支持List.class, Boolean.class和Long.class
RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);
Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),value,expireTime);
if(SUCCESS.equals(result)){
return true;
}
}catch(Exception e){
logger.error("获取分布式锁异常", e);
}
return ret;
}
/**
* 释放锁
* @param lockKey
* @param value
* @return
*/
public boolean releaseLock(String lockKey, String value){
String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
RedisScript<String> redisScript = new DefaultRedisScript<>(script, String.class);
Object result = redisTemplate.execute(redisScript, Collections.singletonList(lockKey),value);
if(SUCCESS.equals(result)) {
return true;
}
return false;
}
@SuppressWarnings("unchecked")
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.redisTemplate = (RedisTemplate<String, T>)beanFactory.getBean("redisTemplate");
}
}
redis客户端
redis不止一个客户端。
Redis Java客户端有很多的开源产品,比如Redission、Jedis、lettuce。
jedis
是最早的客户端,spring-data-redis.jar(即RedisTemplate)早期用这个,后面用lettuce。
Redission
是官方的一个客户端,很晚才出现,主要是为了解决分布式锁问题。
本质
就是两个进程写同一个数据,比如要修改同一条记录数据,就必须使用分布式锁,否则就可能数据被篡改。
如果只是单个进程,只需要使用同步关键字即可。