redis鎖的基本命令
SETNX(SET if Not exist):當(dāng)且僅當(dāng) key 不存在,將 key 的值設(shè)為 value ,并返回1;若給定的 key 已經(jīng)存在,則 SETNX 不做任何動(dòng)作,并返回0。 GETSET:將給定 key 的值設(shè)為 value ,并返回 key 的舊值。先根據(jù)key獲取到舊的value,再set新的value。 EXPIRE 為給定 key 設(shè)置生存時(shí)間,當(dāng) key 過期時(shí),它會(huì)被自動(dòng)刪除。 正確的加鎖方式(很多人以為下面代碼多的是正確的加鎖方式,其實(shí)redis已經(jīng)解決這個(gè)問題了):
// 這里使用集成的jedis jedis.set(String key, String value, String nxxx, String expx, int time) 錯(cuò)誤的加鎖方式1:
//如果程序在執(zhí)行完setnx()之后突然崩潰,導(dǎo)致鎖沒有設(shè)置過期時(shí)間。那么將會(huì)發(fā)生死鎖。Long result = jedis.setnx(Key,value);if(result ==1) {// 若在這里程序突然崩潰,則無法設(shè)置過期時(shí)間,將發(fā)生死鎖jedis.expire(Key, expireTime);}
錯(cuò)誤的加鎖方式2:
? 分布式鎖才用(Key,過期時(shí)間)的方式,如果鎖存在,那么獲取它的過期時(shí)間,如果鎖的確已經(jīng)過期了,那么獲得鎖,并且設(shè)置新的過期時(shí)間
錯(cuò)誤分析:不同的客戶端之間需要同步好時(shí)間。
long expires = System.currentTimeMillis() + expireTime; String expiresStr = String.valueOf(expires);
// 如果當(dāng)前鎖不存在,返回加鎖成功if(jedis.setnx(lockKey, expiresStr) ==1) {returntrue;}// 如果鎖存在,獲取鎖的過期時(shí)間StringcurrentValueStr = jedis.get(lockKey);if(currentValueStr !=null&& Long.parseLong(currentValueStr) < System.currentTimeMillis()) {// 鎖已過期,獲取上一個(gè)鎖的過期時(shí)間,并設(shè)置現(xiàn)在鎖的過期時(shí)間StringoldValueStr = jedis.getSet(lockKey, expiresStr);if(oldValueStr !=null&& oldValueStr.equals(currentValueStr)) {// 考慮多線程并發(fā)的情況,只有一個(gè)線程的設(shè)置值和當(dāng)前值相同,它才有權(quán)利加鎖returntrue;? ? }}// 其他情況,一律返回加鎖失敗returnfalse;
解鎖:判斷鎖的擁有者后可以使用 jedis.del(lockKey) 來釋放鎖。