1.爲何要構建分佈式鎖redis
由於多實例的分佈式運行狀況下,某些代碼塊會存在競爭關係,例如定時關單,假如部署的tomcat集羣爲3臺A,B,C,有一個定時任務,去清除過時訂單,則Tomcat 會出現同時執行某個任務的狀況tomcat
2.redis分佈式鎖原理多線程
使用setnx getset指令分佈式
// @Scheduled(cron="0 */1 * * * ?")//每1分鐘(每一個1分鐘的整數倍) public void closeOrderTaskV3() throws InterruptedException { //防死鎖分佈式鎖 long lockTimeout = Long.parseLong(PropertiesUtil.getProperty("lock.timeout","50000"));//鎖50秒有效期 //項目因爲歷史數據關單訂單比較多,須要處理,初次用50s時間,後續改爲5s便可.同時50s也爲了講課debug的時候時間長而設置。 //你們能夠根據實際狀況,若是歷史訂單都處理完畢,或者在外部進行洗數據ok,這裏的lock的時間應該設置小一些,例如1s 2s 3s 4s 5s就足夠啦。 //這個時間如何用呢,看下面。和時間戳結合起來用。 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{ //若是setnxResult==null 或 setnxResult.intValue() ==0 即 != 1的時候 //未獲取到鎖,繼續判斷,判斷時間戳,看是否能夠重置獲取到鎖 String lockValueStr = RedisShardedPoolUtil.get(Const.REDIS_LOCK.CLOSE_ORDER_TASK_LOCK); //若是lockValue不是空,而且當前時間大於鎖的有效期,說明以前的lock的時間已超時,執行getset命令. 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值,獲取舊的值。 //運用多線程的CAS原理,只有在舊值沒有被修改的狀況下,才能拿到鎖 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); } }