這篇文章主要介紹“Java中redis分布式鎖的實現(xiàn)方法”,在日常操作中,相信很多人在Java中redis分布式鎖的實現(xiàn)方法問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Java中redis分布式鎖的實現(xiàn)方法”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
成都創(chuàng)新互聯(lián)公司主營瑪曲網(wǎng)站建設(shè)的網(wǎng)絡公司,主營網(wǎng)站建設(shè)方案,成都app軟件開發(fā)公司,瑪曲h5重慶小程序開發(fā)搭建,瑪曲網(wǎng)站營銷推廣歡迎瑪曲等地區(qū)企業(yè)咨詢
分布式鎖是用于解決多個進程互斥地訪問共享資源時產(chǎn)生的問題。
舉個例子,當你做新增操作時,為了防止重復插入,需要進行“查找->是否存在->不存在->添加”的邏輯判斷。
如果有兩個線程同時進入這個邏輯判斷,那么兩個線程同時進入“不存在”這個判斷時,就會有兩次插入操作。
一般為了解決這種問題,Java中可以用synchronized等線程安全的方法來解決,但是當應用是多實例部署時,就需要用分布式鎖來解決了。
1.基于數(shù)據(jù)庫
2.基于redis
3.基于中間件(zookeeper)
使用分布式鎖需要滿足以下三個條件
1.互斥。即鎖只能由一個線程獲取。
2.不會死鎖。線程崩潰等原因?qū)е麻L時間獲取鎖不釋放,要有機制能自動釋放鎖。
3.不能誤解鎖。加鎖和解鎖必須要是同一個線程。
為什么使用redis作為分布式鎖呢?
最主要的原因是redis是單線程訪問的,指令是按隊列一條條順序執(zhí)行。
我們只需要把上鎖這個操作的指令寫成一個腳本,讓redis執(zhí)行即可。
鎖對象包括兩個值key和requestId。
key即為鎖的key,一般是場景+唯一標識
requestId是鎖的值,用于解鎖時能保證不會誤解鎖,一般是key+時間戳。
public RedisLockBean getLockBean(String lock, String id){ String LockKey = lock.concat(id); String requestId = LockKey.concat(String.valueOf(System.currentTimeMillis())); return new RedisLockBean().setKey(LockKey).setRequestId(requestId); }
使用代碼執(zhí)行命令
SET key requestId NX PX 10000
NX: 如果不存在就設(shè)置
PX milliseconds: 鍵的過期時間設(shè)置為多少毫秒
private static final String SET_IF_ABSENT = "NX"; private static final String SET_WITH_EXPIRE_TIME = "PX"; private Long expireTime = 10000L; public Boolean setLock(RedisLockBean lockBean){ RedisCallbackstringRedisCallback = (connection) ->{ JedisCommands commands = (JedisCommands) connection.getNativeConnection(); return commands.set(lockBean.getKey(), lockBean.getRequestId(), SET_IF_ABSENT, SET_WITH_EXPIRE_TIME, expireTime); }; String result = (String) redisTemplate.execute(stringRedisCallback); return !StringUtils.isEmpty(result); }
執(zhí)行命令
if redis.call("get",KEYS[1]) == ARGV[1] then return redis.call("del", KEYS[1]) else return 0 end
private static final String UNLOCK_LUA; static { StringBuilder sb = new StringBuilder(); sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] "); sb.append("then "); sb.append(" return redis.call(\"del\",KEYS[1]) "); sb.append("else "); sb.append(" return 0 "); sb.append("end "); UNLOCK_LUA = sb.toString(); } public Boolean releaseLock(RedisLockBean lockBean){ // 釋放鎖的時候,有可能因為持鎖之后方法執(zhí)行時間大于鎖的有效期,此時有可能已經(jīng)被另外一個線程持有鎖,所以不能直接刪除 try { Listkeys = new ArrayList<>(); keys.add(lockBean.getKey()); List args = new ArrayList<>(); args.add(lockBean.getRequestId()); // 使用lua腳本刪除redis中匹配value的key,可以避免由于方法執(zhí)行時間過長而redis鎖自動過期失效的時候誤刪其他線程的鎖 // spring自帶的執(zhí)行腳本方法中,集群模式直接拋出不支持執(zhí)行腳本的異常,所以只能拿到原redis的connection來執(zhí)行腳本 RedisCallback callback = (connection) -> { Object nativeConnection = connection.getNativeConnection(); // 集群模式和單機模式雖然執(zhí)行腳本的方法一樣,但是沒有共同的接口,所以只能分開執(zhí)行 // 集群模式 if (nativeConnection instanceof JedisCluster) { return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args); } // 單機模式 else if (nativeConnection instanceof JedisCommands) { return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args); } return 0L; }; Long result = (Long) redisTemplate.execute(callback); return result != null && result > 0; } catch (Exception e) { log.error("release lock occured an exception", e); } return false; }
到此,關(guān)于“Java中redis分布式鎖的實現(xiàn)方法”的學習就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關(guān)知識,請繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
當前名稱:Java中redis分布式鎖的實現(xiàn)方法
URL地址:http://weahome.cn/article/giggii.html