獲取鎖實現原理 使用redis lua腳本 lua腳本執行是個原子操作
重要命令 setnx 如果key不存在則設置值
代碼:
/**
* 獲取鎖
* 原因是 redis是單線程的 一但一個線程獲取鎖後 其它線程就不能獲取的鎖
* @param lockKey 鎖名稱
* @param value 值
* @param expire 過期時間
* @param timeout 等待超時時間
* @return
*/
public boolean getLock(String lockKey,String value ,String expire, long timeout){
//獲取當前毫米數
long startTime = System.currentTimeMillis();
try{
while (true){
String script = "if redis.call('setNx',KEYS[1],ARGV[1]) then " +
"if redis.call('get',KEYS[1]) == ARGV[1] then " +
"return redis.call('expire',KEYS[1],ARGV[2]) else return 0 end end";
RedisScript<Long> redisScript = new DefaultRedisScript<>(script,Long.class);
List<String> keys = new ArrayList<>();
keys.add(lockKey);
Long execute = redisTemplate.execute(redisScript, keys, value, expire);
if (1l == execute.longValue()){
return true;
} else {
long endTime = System.currentTimeMillis();
if ((endTime - startTime) > timeout){
return false;
}
}
}
}catch (Exception e){
e.printStackTrace();
return false;
}
}
釋放鎖:
/**
* 釋放鎖
* @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<Long> redisScript = new DefaultRedisScript<>(script,Long.class);
List<String> keys = new ArrayList<>();
keys.add(lockKey);
Long execute = redisTemplate.execute(redisScript, keys, value);
if (1l == execute.longValue()){
return true;
}
return false;
}
測試案例:
public void getLock(){
String key = "aLock";
String value = "1";
try {
boolean flag = redisUtil.getLock(key,value,"10",20000);
if (flag){
System.out.println("獲取鎖 處理業務邏輯");
} else {
System.out.println("獲取鎖 處理業務超時");
}
}finally {
boolean b = redisUtil.releaseLock(key, value);
if (b){
System.out.println("釋放鎖成功");
} else {
System.out.println("釋放鎖失敗");
}
}
}
注意:expire 要爲字符串類型 原因:當參數轉換爲byte數組的時候,會將非字符串轉換爲字符串時報轉換異常。