利用redis實現分布式鎖
SETNX 并不難完美實現(不帶過期時間),SETNX 實現鎖有陷阱需謹慎
SETEX 復寫,帶過期時間(原子) </div>
SETEX 復寫,帶過期時間(原子) </div>
分布式鎖工具
private static Logger logger = Logger.getLogger(LockUtils.class); /** * 最長時間鎖為1天 */ private final static int maxExpireTime = 24 * 60 * 60; /** * 系統時間偏移量15秒,服務器間的系統時間差不可以超過15秒,避免由于時間差造成錯誤的解鎖 */ private final static int offsetTime = 15; /** * 鎖只是為了解決小概率事件,最好的方式是不用,從設計上避免分布式鎖 * * @param key * key * @param value * @param waitTime * 秒 - 最大等待時間,如果還無法獲取,則直接失敗 * @param expire * 秒- 鎖生命周期時間 * @return true 成功 false失敗 * @throws Exception */ public static boolean Lock(String key, String value, int waitTime, int expire) { long start = System.currentTimeMillis(); String lock_key = key + "_lock"; logger.info("開始獲取分布式鎖 key:" + key + " lock_key:" + lock_key + " value:" + value); do { try { Thread.sleep(1); long ret = CacheUtils.Setnx(CacheSpacePrefixEnum.TOOLBAR_SYS.name(), lock_key, System.currentTimeMillis() + "$T$" + value, (expire > maxExpireTime) ? maxExpireTime : expire); if (ret == 1) { logger.info("成功獲得分布式鎖 key:" + key + " value:" + value); return Boolean.TRUE; } else { // 存在鎖,并對死鎖進行修復 String desc = CacheUtils.GSetnx(CacheSpacePrefixEnum.TOOLBAR_SYS.name(), lock_key); // 首次鎖檢測 if (desc.indexOf("$T$") > 0) { // 上次鎖時間 long lastLockTime = NumberUtils.toLong(desc.split("[$T$]")[0]); // 明確死鎖,利用Setex復寫,再次設定一個合理的解鎖時間讓系統正常解鎖 if (System.currentTimeMillis() - lastLockTime > (expire + offsetTime) * 1000) { // 原子操作,只需要一次,【任然會發生小概率事件,多個服務同時發現死鎖同時執行此行代碼(并發), // 為什么設置解鎖時間為expire(而不是更小的時間),防止在解鎖發送錯亂造成新鎖解鎖】 CacheUtils.Setex(CacheSpacePrefixEnum.TOOLBAR_SYS.name(), lock_key, value, expire); logger.warn("發現死鎖【" + expire + "秒后解鎖】key:" + key + " desc:" + desc); } else { logger.info("當前鎖key:" + key + " desc:" + desc); } } else { logger.warn("死鎖解鎖中key:" + key + " desc:" + desc); } } if (waitTime == 0) { break; } Thread.sleep(500); } catch (Exception ex) { logger.error(Trace.GetTraceStackDetails("獲取鎖失敗", ex)); } } while ((System.currentTimeMillis() - start) < waitTime * 1000); logger.warn("獲取分布式鎖失敗 key:" + key + " value:" + value); return Boolean.FALSE; } /** * 解鎖 * * @param key * @return * @throws Exception */ public static boolean UnLock(String key) { String lock_key = key + "_lock"; try { CacheUtils.Del(CacheSpacePrefixEnum.TOOLBAR_SYS.name(), lock_key); } catch (Exception ex) { logger.error(Trace.GetTraceStackDetails("解鎖鎖失敗key:" + key + " lock_key:" + lock_key, ex)); } return Boolean.FALSE; }
redis操作分裝部分
@Override public Long Setnx(String key, String value, int expireTime) throws Exception { ShardedJedis jedis = null; try { jedis = pool.getResource(); Long ret = jedis.setnx(key, value); if (ret == 1 && expireTime > 0) { jedis.expire(key, expireTime); } return ret; } catch (Exception e) { throw e; } finally { if (pool != null && jedis != null) { pool.returnResourceObject(jedis); } } } @Override public String Setex(String key, String value, int expireTime) throws Exception { ShardedJedis jedis = null; try { jedis = pool.getResource(); String ret = jedis.setex(key, expireTime, value); return ret; } catch (Exception e) { throw e; } finally { if (pool != null && jedis != null) { pool.returnResourceObject(jedis); } } } @Override public String GSetnx(String key) throws Exception { ShardedJedis jedis = null; try { jedis = pool.getResource(); return jedis.get(key); } catch (Exception e) { throw e; } finally { if (pool != null && jedis != null) { pool.returnResourceObject(jedis); } } }
本文由用戶 RegPoate 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!