設計注意點
1.鎖要有超時時間 setnx 加時間
2.鎖要釋放 finally
3.藉助threadlocal避免線程錯亂解鎖
public class DistributedLockService {
private static final String LOCK_SUCCESS = "OK";
private static final String SET_IF_NOT_EXIST = "NX";
private static final String SET_WITH_EXPIRE_TIME = "EX";
private static final Long RELEASE_SUCCESS = 1L;
static String RELEASE_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
@Autowired
private JedisFacade jedisFacade;
private final Logger logger = LoggerFactory.getLogger(DistributedLockService.class);
private ThreadLocal<String> tokenMap = new ThreadLocal<>();
public boolean tryAcquire(String key , Long timeOutSeconds){
String token = tokenMap.get();
if (StringUtils.isBlank(token)) {
token = UUID.randomUUID().toString().replaceAll("-","");
tokenMap.set(token);
}
String result = jedisFacade.set(key , token , SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, timeOutSeconds);
boolean flag = StringUtils.equalsIgnoreCase(LOCK_SUCCESS , result);
if(!flag){
String origToken = jedisFacade.get(key);
if (StringUtils.isBlank(origToken)) {
result = jedisFacade.set(key, token, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, timeOutSeconds);
if (StringUtils.equalsIgnoreCase(LOCK_SUCCESS , result)){
flag = true;
}
}else if(token.equals(origToken)){
flag = true;
}
}
return flag;
}
public boolean tryRelease(String distributeKey) {
String token = tokenMap.get();
String origToken = jedisFacade.get(distributeKey);
//如果爲空 則返回解鎖成功 可能是狗屎運-並不是自己完成解鎖的
if(StringUtils.isBlank(origToken)){
logger.info("鎖超時釋放..");
return true;
}
//檢查當前解鎖的線程的合法性
if(StringUtils.isBlank(token) || !StringUtils.equals(origToken , token)){
return false;
}
Object result = jedisFacade.eval(RELEASE_SCRIPT , Collections.singletonList(distributeKey), Collections.singletonList(token));
if (RELEASE_SUCCESS.equals(result)) {
return true;
}
return false;
}
}