redis(千帆竞发--分布式锁)
比如一個(gè)操作要修改的用戶的狀態(tài),修改狀態(tài)需要先讀出用戶的狀態(tài),在內(nèi)存里進(jìn)行修改,改完了再存回去。
如果這樣的操作同時(shí)進(jìn)行了,就會(huì)出現(xiàn)并發(fā)問(wèn)題,因?yàn)樽x取和保存狀態(tài)這兩個(gè)操作不是原子的。
分布式鎖:
分布式鎖本質(zhì)上要實(shí)現(xiàn)的目標(biāo)就是在Redis里面占“坑”,當(dāng)別的進(jìn)程也要來(lái)占時(shí),發(fā)現(xiàn)已經(jīng)有人蹲在那里了,就
只好放棄或者稍后再試。
占坑一般是使用setnx(set if not exists)指令,只允許被一個(gè)客戶端占坑。先來(lái)先到,用完了在調(diào)用del指令釋放。
>setnx lock:codehole true OK ...do something critical... >del lock:codehole (integer) 1但有個(gè)問(wèn)題,如果邏輯執(zhí)行到中間出現(xiàn)異常,可能會(huì)導(dǎo)致del指令沒(méi)有被調(diào)用,這樣就會(huì)陷入死鎖,鎖就得不到釋放了。
這樣我們可以拿到鎖之后,再給鎖加上一個(gè)過(guò)期時(shí)間,for example:5s
>setnx lock:codehole true OK >expire lock:codehole 5 ...do something critical... >del lock:codehole (integer) 1如果在setnx和expire之間服務(wù)器進(jìn)程突然掛掉的,就會(huì)導(dǎo)致expire得不到執(zhí)行,也會(huì)造成死鎖。這個(gè)問(wèn)題根源在于setnx和expire是兩條指令而不是原子指令。如果這兩條指令可以一起執(zhí)行就不會(huì)出現(xiàn)問(wèn)題。
>set lock:codehole true ex 5 nx OK ...do something critical... >del lock:codehole上面這個(gè)指令就是setnx和expire組合一起的原子指令,它就是分布式鎖奧義的所在。
?
?
超時(shí)問(wèn)題:
Redis的分布式鎖解決超時(shí)問(wèn)題,如果在加鎖和釋放鎖之間的邏輯執(zhí)行的太長(zhǎng),以至于超出了鎖的超時(shí)限制,就會(huì)出現(xiàn)問(wèn)題。因?yàn)?/p>
這時(shí)候第一個(gè)線程持有的鎖過(guò)期了,臨界區(qū)的邏輯還沒(méi)有執(zhí)行完,這個(gè)時(shí)候第二個(gè)線程就提前重新持有了這把鎖,導(dǎo)致臨界區(qū)代碼不能得到嚴(yán)格的串行執(zhí)行。
lua 腳本if redis.call("get",keys[1] == argv[1] then return redis.call("del",keys[1]))else return 0 end可重入性:
可重入性是指線程在持有鎖的情況下再次請(qǐng)求加鎖,如果一個(gè)鎖支持同一個(gè)線程的多次加鎖,那么這個(gè)鎖就是可重入的。
public class RedisWithReentrantLock{private ThreadLocal<Map<String,Integer>> lockers=new ThreadLocal<>();private Jedis jedis;public RedisWithReentrantLock(Jedis jedis){this.jedis=jedis;}private boolean lock(String key){return jedis.set(key,"","nx","ex",5L)}private void unlock(){jedis.del(key);}private Map<String,Integer> currentLockers(){Map<String,Integer> refs=lockers.get();if(refs!=null){return refs;}lockers.set(new HashMap<>());return lockers.get();}public boolean lock(String key){Map<String,Integer> refs=currentLockers();Integer refCnt=refs.get(key);if(refCnt!=null){refs.put(key,refCnt+1);return true;}boolean ok=this.lock(key);if(!ok){return false;}refs.put(key,1);return true; }public boolean unlock(String key){Map<String,Integer> refs=currentLockers(); Integer refCnt=refs.get(key);if(refCnt ==null){return false;}refCnt-=1;if(refCnt>0){refs.put(key,refCnt);}else{refs.remove(key);this.unlock(key);}return true;} public static void main(String[] args){Jedis jedis=new Jedis();RedisWithReentrantLock redis=new RedisWithReentrantLock(jedis);System.out.println(redis.lock("codehole"));System.out.println(redis.lock("codehole"));System.out.println(redis.unlock("codehole"));System.out.println(redis.unlock("codehole"));} }
?
總結(jié)
以上是生活随笔為你收集整理的redis(千帆竞发--分布式锁)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Epicor客制化 - 在VS中进行开发
- 下一篇: 支付常识