分佈式鎖:當多個進程不在同一個系統中,用分佈式鎖控制多個進程對資源的訪問
分佈式鎖應該具備哪些條件
- 在分佈式系統環境下,一個方法在同一時間只能被一個機器的一個線程執行
- 高可用的獲取鎖與釋放鎖
- 高性能的獲取鎖與釋放鎖
- 具備可重入特性(可理解爲重新進入,由多於一個任務併發使用,而不必擔心數據錯誤)
- 具備鎖失效機制,防止死鎖
- 具備非阻塞鎖特性,即沒有獲取到鎖將直接返回獲取鎖失敗
import com.demo.common.RedissonManager;
import org.redisson.api.RLock;
@Component
@Slf4j
public class CloseOrderTask {
@Autowired
private IOrderService iOrderService;
@Autowired
private RedissonManager redissonManager;
@PreDestroy
public void delLock(){
RedisShardedPoolUtil.del(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}
// @Scheduled(cron="0 */1 * * * ?")//每1分鐘(每個1分鐘的整數倍)
public void closeOrderTaskV1(){
log.info("關閉訂單定時任務啓動");
int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
// iOrderService.closeOrder(hour);
log.info("關閉訂單定時任務結束");
}
// @Scheduled(cron="0 */1 * * * ?")
public void closeOrderTaskV2(){
log.info("關閉訂單定時任務啓動");
long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("lock.timeout","5000"));
Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout));
if(setnxResult != null && setnxResult.intValue() == 1){
//如果返回值是1,代表設置成功,獲取鎖
closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}else{
log.info("沒有獲得分佈式鎖:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}
log.info("關閉訂單定時任務結束");
}
@Scheduled(cron="0 */1 * * * ?")
public void closeOrderTaskV3(){
log.info("關閉訂單定時任務啓動");
long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("lock.timeout","5000"));
Long setnxResult = RedisShardedPoolUtil.setnx(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout));
if(setnxResult != null && setnxResult.intValue() == 1){
closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}else{
//未獲取到鎖,繼續判斷,判斷時間戳,看是否可以重置並獲取到鎖。如果當前時間已經大於之前設置的時間則可以失效了
String lockValueStr = RedisShardedPoolUtil.get(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
if(lockValueStr != null && System.currentTimeMillis() > Long.parseLong(lockValueStr)){
String getSetResult = RedisShardedPoolUtil.getSet(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTimeout));
//再次用當前時間戳getset。
//返回給定的key的舊值,->舊值判斷,是否可以獲取鎖
//當key沒有舊值時,即key不存在時,返回nil ->獲取鎖
//這裏我們set了一個新的value值,獲取舊的值。
if(getSetResult == null || (getSetResult != null && StringUtils.equals(lockValueStr,getSetResult))){
//真正獲取到鎖
closeOrder(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}else{
log.info("沒有獲取到分佈式鎖:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}
}else{
log.info("沒有獲取到分佈式鎖:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
}
}
log.info("關閉訂單定時任務結束");
}
// @Scheduled(cron="0 */1 * * * ?")
public void closeOrderTaskV4(){
RLock lock = redissonManager.getRedisson().getLock(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
boolean getLock = false;
try {
//注意這個0
if(getLock = lock.tryLock(0,50, TimeUnit.SECONDS)){
log.info("Redisson獲取到分佈式鎖:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
// iOrderService.closeOrder(hour);
}else{
log.info("Redisson沒有獲取到分佈式鎖:{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
}
} catch (InterruptedException e) {
log.error("Redisson分佈式鎖獲取異常",e);
} finally {
if(!getLock){
return;
}
lock.unlock();
log.info("Redisson分佈式鎖釋放鎖");
}
}
private void closeOrder(String lockName){
RedisShardedPoolUtil.expire(lockName,5);//有效期50秒,防止死鎖
log.info("獲取{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
int hour = Integer.parseInt(PropertiesUtil.getProperty("close.order.task.time.hour","2"));
iOrderService.closeOrder(hour);
RedisShardedPoolUtil.del(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK);
log.info("釋放{},ThreadName:{}",Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK,Thread.currentThread().getName());
log.info("===============================");
}
}