加鎖實現
實現邏輯
通過for循環自旋的方式,判斷redis中是否存在鎖的緩存,存在則放回true,否則判斷獲取鎖的時間是否超時,超時則返回false。
自旋的判斷時間是很快的,設置的超時時間如果太長會占用cpu的時間片處理。
加鎖的實現方法
/**
* 獲取鎖的超時時間
*/
private static final long timeout = 300;
/**
* 加鎖,無阻塞
* @param key
* @param expireTime
* @return
*/
public Boolean lock(String key, long expireTime) {
String requestId = UUID.randomUUID().toString();
Long start = System.currentTimeMillis();
//自旋,在一定時間內獲取鎖,超時則返回錯誤
for (;;){
//Set命令返回OK,則證明獲取鎖成功
Boolean ret = redisTemplate.opsForValue().setIfAbsent( key, requestId, expireTime,
TimeUnit.SECONDS);
if (ret) {
return true;
}
//否則循環等待,在timeout時間內仍未獲取到鎖,則獲取失敗
long end = System.currentTimeMillis() - start;
if (end >= timeout){
return false;
}
}
}
解鎖實現
實現邏輯
直接刪除鎖的緩存即可
解鎖的實現方法
/**
* 刪除緩存
*
* @param key 可以傳一個值 或多個
*/
public void del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
redisTemplate.delete(key[0]);
} else {
redisTemplate.delete(CollectionUtils.arrayToList(key));
}
}
}
業務代碼的實現
實現邏輯
先判斷是否獲得鎖,是則處理業務代碼,並且在最后進行解鎖操作,否則循環5次,繼續獲取鎖,能獲取到則實現業務方法,不能獲取到則返回錯誤信息
實現方法
/**
* 鎖的key前綴
*/
private static final String lockStr = "r_lock_";
/**
* 獲取鎖最大失敗次數
*/
private static final int maxFailCount = 5;
@RequestMapping("/t1")
public void testRedis(){
String key = "test";
// 鎖的過期時間,避免死鎖,根據業務調整,毫秒
long expireTime = 1000;
//判斷是否獲得鎖
boolean lock = redisService.tryLock(lockStr + key,expireTime);
if(lock){
try {
String result = redisService.get(key);
//處理業務的方法
//TODO
redisService.set(key,result);
} catch (Exception e) {
e.printStackTrace();
} finally {
//解鎖
redisService.del(lockStr + key);
}
}else{
int failCount = 1;
while (failCount <= maxFailCount){
//判斷是否獲得鎖
if (redisService.tryLock(lockStr + key,expireTime)){
//處理業務的方法
//TODO
//解鎖
redisService.del(lockStr + key);
}else{
failCount ++;
}
}
throw new RuntimeException("當前創建的數量太多了,請稍后再試。");
}
}