网上也流传着很多分布试锁的实现方式,最近在读Redis in Action。里面也提到了分布式锁,我以前用redis实现锁,也只是实现一个简单的原子操作。并没用过太过复杂的方式,也没有实现阻塞。最近我想再写一个锁,不过当然跟一般的分布式锁不同。我的思路是:如果同一台机器多个线程判断资源有没有没占用,就没必要去redis上去查找了,直接查看本地标识。
import java.util.concurrent.ConcurrentHashMap;
public class MapLockSupport implements LockSupport {
private ConcurrentHashMap<String,LockInfo> locksMap = new ConcurrentHashMap<>();
public MapLockSupport() {
}
@Override
public boolean tryLock(String resourceId,final long expire) {
LockInfo lockInfo ;
do {
lockInfo = locksMap.putIfAbsent(resourceId, new LockInfo(resourceId, expire)); //返回null代表添加成功
if (lockInfo == null) return true;
try {
lockInfo = locksMap.computeIfPresent(resourceId, (k, v) -> { // 判断是否超时
if (v.getExpireTimeStamp() < System.currentTimeMillis()) {
v.setExpire(expire);
} else {
throw new TimeoutException();
}
return v;
});
}catch (TimeoutException ex){
return false;
}
}while(lockInfo == null);
return true;
}
@Override
public void releaseLock(String resouceId) {
locksMap.remove(resouceId);
}
public static class TimeoutException extends RuntimeException{
}
}
我用ConcurrentHashMap的原子方法,实现对本地资源的标识
package com.example.demo.lock;
import org.springframework.data.redis.core.RedisTemplate;
import java.time.Duration;
import java.util.concurrent.TimeUnit;
public class RedisLockSupport implements LockSupport {
private RedisTemplate redisTemplate ;
public RedisLockSupport(RedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public boolean tryLock(String resourceId, long expire) {
if(redisTemplate.opsForValue().setIfAbsent(resourceId, expire, Duration.ofMillis(expire))){
return true;
}
return false;
}
@Override
public void releaseLock(String resourceId) {
redisTemplate.delete(resourceId);
}
}
redis是真正的分布式锁,我实现的很简单。
public class ComposeLockSupport implements LockSupport {
private MapLockSupport mapLockSupport;
private RedisLockSupport redisLockSupport;
public ComposeLockSupport(MapLockSupport mapLockSupport, RedisLockSupport redisLockSupport) {
this.mapLockSupport = mapLockSupport;
this.redisLockSupport = redisLockSupport;
}
@Override
public boolean tryLock(String resourceId, long expire) {
if(mapLockSupport.tryLock(resourceId,expire)){
if(redisLockSupport.tryLock(resourceId,expire)){
return true;
}else{
mapLockSupport.releaseLock(resourceId);
}
}
return false;
}
@Override
public void releaseLock(String resourceId) {
redisLockSupport.releaseLock(resourceId);
mapLockSupport.releaseLock(resourceId);
}
}
最后我将这两段代码组合在一起。以后整理