一、为什么需要使用分布式锁呢?
在分布式系统中,或者传统集群部署项目中,在并发需要保证资源安全使用的情况。jvm中的锁没有办法真正保证资源安全的。至于为什么呢?简单理解为jvm锁只能在各自的容器中实现锁机制,在自身的jvm容器之前也就没有办法控制了。
二、分布式锁实现需要注意些什么东西呢?
这个是参考的一篇博客,里面说到的比较完善,建议大家看看,与此同时也十分感谢该博客的作者。
https://www.jianshu.com/p/a1ebab8ce78a
我个人觉得里面讲得不错。自己也在自己的理解上居于redis实现了一个通用的分布式锁组件(工具类)
三、个人实现的分布式锁主要代码如下,为了保证原子性,我使用了lua脚本
/**
* @Author: ChenHuaMing
* @Date: 2020/10/9 14:53
* @Description: 分布式锁(redis实现)
*/
public class RedisLockServiceImpl implements ILockService {
private final Logger logger= LoggerFactory.getLogger(RedisLockServiceImpl.class);
private StringRedisTemplate stringRedisTemplate;
private RedisScript<Boolean> defaultRedisScript;
private String timeOut;
private ObjectMapper objectMapper=new ObjectMapper();
private final String PREFIX="hadoop:lock:";
public RedisLockServiceImpl(StringRedisTemplate stringRedisTemplate, RedisScript<Boolean> defaultRedisScript, String timeOut) {
this.stringRedisTemplate = stringRedisTemplate;
this.defaultRedisScript = defaultRedisScript;
this.timeOut = timeOut;
}
@Override
public boolean getLock(String key,String expireTime) {
if(StringUtils.isEmpty(expireTime)){
expireTime=getTimeOut();
}
long lockTime = System.currentTimeMillis();
LockValue redisLockValue = new LockValue(key,getThreadId(), lockTime, (lockTime + Long.valueOf(expireTime)));
try {
String value = objectMapper.writeValueAsString(redisLockValue);
List<String> keys = Arrays.asList(PREFIX+key, value);
Boolean execute = stringRedisTemplate.execute(defaultRedisScript, keys, expireTime);
return execute == null?false:execute;
} catch (JsonProcessingException e) {
logger.error("transport lockValue error in lock",e);
return false;
}
}
@Override
public boolean unlock(String key) {
String lockValueStr = stringRedisTemplate.opsForValue().get(PREFIX + key);
try {
if(!StringUtils.isEmpty(lockValueStr)){
LockValue lockValue = objectMapper.readValue(lockValueStr, LockValue.class);
if(!getThreadId().equals(lockValue.getThreadId())){
throw new RuntimeException("线程id不一致,不允许解锁");
}
if(!lockValue.getBussId().equals(key)){
throw new RuntimeException("业务id不一致,不允许解锁");
}
return stringRedisTemplate.delete(PREFIX+key);
}else{
return false;
}
} catch (JsonProcessingException e) {
logger.error("transport lockValue error in unlock",e);
return false;
}
}
private String getThreadId(){
return new StringBuilder()
.append(IpUtils.getLocalIpAddress())
.append("-")
.append(Thread.currentThread().getId()).toString();
}
@Override
public String getTimeOut() {
return timeOut;
}
}
--- 获取key
local key = KEYS[1]
--- 获取value
local val = KEYS[2]
--- 获取一个参数
local expire = ARGV[1]
--- 如果redis找不到这个key就去插入
if redis.call("get", key) == false then
--- 如果插入成功,就去设置过期值
if redis.call("set", key, val) then
if tonumber(expire) > 0 then
--- 设置过期时间
redis.call("expire", key, expire)
end
return true
end
return false
else
return false
end
对应源码我放在了码云上,有需要的朋友可以自行下载
https://gitee.com/MingAndTao/ljm-simple-base/tree/master/ljm/ljm-cache/src/main/java/com/ljm/lock
我是按组件方式实现的,觉得可以的朋友,可以直接拷贝组件是使用
四、redis方式实现的分布式锁组件使用方法
使用方法请参考这个wiki:https://gitee.com/MingAndTao/ljm-simple-base/wikis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81%E4%BD%BF%E7%94%A8?sort_id=2969890
博客写得比较简陋,请多多包涵