項目開發中我們經常需要用到分佈式鎖,尤其是在集羣環境多臺服務器情況下,比如 接口請求、定時任務等業務環境中;
今天 我們先介紹一種 redis 分佈式鎖,在普通項目中就可以使用,代碼如下:
1、redis 加鎖和釋放鎖代碼如下:
package com.jy.utils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCommands;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
/**
* RedisTemplate工具類 通過註解(@Component)掃描此類
*/
@Component
public class RedisUtil implements ApplicationContextAware {
private static final Logger logger = LoggerFactory.getLogger(RedisUtil.class);
private static RedisTemplate<Object, Object> redisTemplate;//redis 模板類
private static ThreadLocal<String> lockFlag = new ThreadLocal<String>();
public static final String UNLOCK_LUA;
//redis 加鎖方法
public static Boolean setNX(String key,long timeout) {
String value = String.valueOf(System.currentTimeMillis());//鎖的value 值設置
Boolean isExit = false;
try {
//設置鎖的核心代碼
isExit = redisTemplate.getConnectionFactory().getConnection().setNX(key.getBytes(), value.getBytes());
/** 如果設置成功,要設置其過期時間 */
if (isExit) {
redisTemplate.expire(key, timeout, TimeUnit.SECONDS);
logger.info(key+":成功獲取到鎖");
return isExit;
}
}catch (Exception e){
logger.info(key+":獲取鎖失敗",e);
}
logger.info(key+":未獲取到鎖");
return isExit;
}
//釋放鎖的核心方法
public static void releaseLock(String key ){
try {
redisTemplate.opsForValue().getOperations().delete(key);//直接刪除key
logger.info(key+";鎖釋放成功");
} catch (Exception ex) {
logger.error(key+";鎖釋放失敗",ex);
}
}
//通過springIOC容器取redis模板類
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
redisTemplate = applicationContext.getBean(RedisTemplate.class);
}
}
2、redis 分佈式鎖就這麼簡單,但是!此種方案是有有坑的,在普通項目、併發量小、定時任務頻率低、服務穩定的的情況下勉強可以使用。分析一下原因:首先,該加鎖過程不能保證原子性,比如剛剛加鎖之後,還沒有來得及設置超時時間,服務出問題了,或者中間出現了異常代碼,那麼這個鎖沒有設置超時時間,就可能成爲死鎖(如果業務代碼中沒有成功釋放鎖)。
其次:關於釋放鎖,如果在多線程情況下,可能會把其他線程的鎖(同一個key : 鎖)釋放掉,這對業務來說是非常危險的。
下篇我將介紹另一種redis 分佈式鎖,能很好的彌補這種方案的漏洞,敬請期待!下篇地址:
https://blog.csdn.net/nandao158/article/details/105866418