什么是分布式鎖:
有分布式鎖就有單機鎖,按照我的理解單機鎖就是服務器在一個單一節(jié)點上虎眨,當處理高并發(fā)的問題時我們可以利用可重入鎖(ReentrantLock類和synchronized),但是分布式的同步問題非常突出,不同的服務是分布在不同的節(jié)點上钦扭,也就是不同的服務器,節(jié)點和節(jié)點之前的JVM無法解決同步問題
分布式鎖的三種實現(xiàn)
基于數(shù)據(jù)庫的分布式鎖:一般有悲觀鎖和樂觀鎖的兩種實現(xiàn)機制
樂觀鎖的實現(xiàn)是這樣的(基于我比較懶就直接寫文字了):
樂觀鎖的特點是認為數(shù)據(jù)不會遭到修改床绪,也就是在操作的最后一步才會進行同步的處理客情。樂觀鎖就是在數(shù)據(jù)庫增加version的字段,操作的線程每次操作前讀取version癞己,更新數(shù)據(jù)之前再次查看version膀斋,如果一樣就更新數(shù)據(jù),否則更新失敗
想象一下不同的兩個人同時對一個賬戶進行取款操作痹雅,同時會讀取到數(shù)據(jù)中的余額仰担,version假設是1,然后其中一個操作進行了取款练慕,再次查看version還是1惰匙,然后把更新后的version+1和余額又寫入數(shù)據(jù)庫技掏,這是version為2铃将。那么另一個操作雖然一開始讀到version是1,但是當更新完數(shù)據(jù)后查看version變成2哑梳,因為這時前一操作已經更改完畢劲阎,那么這次取款就操作失敗,也就是說需要重新讀取賬戶余額鸠真。
樂觀鎖操作條件:具備遞增的version字段悯仙、每次更新前查看version是否正確龄毡,再進行更新
悲觀鎖的實現(xiàn)其實是用到了for update語句,一般用在JDBC鏈接時锡垄,當我們執(zhí)行的查詢語句最后包含for update的時候執(zhí)行此語句的線程就獲得了排它鎖沦零,我們認為這個時候此線程獲得了悲觀鎖
當我們在語句后用了for update時,多個線程執(zhí)行這條語句的情況下货岭,只有一個線程會獲取到鎖路操,其它線程被阻塞掛起,獲取鎖的線程執(zhí)行傳遞的callback 的業(yè)務邏輯千贯,執(zhí)行完畢后 執(zhí)行commit 提交事務屯仗,這意味著當前線程釋放了獲取的鎖,這時候被阻塞的線程會競爭獲取該鎖搔谴。
基于Redis的分布式鎖:setx命令
簡單說一個線程對Redis進行加鎖魁袜,原理是設置一個不存在的key和value,設置成功后返回1敦第,這時其他線程無法再次設置這個不存在的key因為此key已存在就會返回0
以下命令是Redis從2.6.12版本開始支持的:
set key value NX PX n
其中set key value NX
相當于以前的setnx key value
峰弹,px
是設置過期時間
為什么這個命令可以幫我們實現(xiàn)鎖機制呢?
因為這個命令是只有在某個key不存在的時候芜果,才會執(zhí)行成功垮卓。那么當多個進程同時并發(fā)的去設置同一個key的時候,就永遠只會有一個進程成功师幕。
當某個進程設置成功之后粟按,就可以去執(zhí)行業(yè)務邏輯了,等業(yè)務邏輯執(zhí)行完畢之后霹粥,再去進行解鎖灭将。
解鎖很簡單,只需要刪除這個key就可以了后控,不過刪除之前需要判斷庙曙,這個key對應的value是當初自己設置的那個。
基于zookeeper的分布式鎖
zookeeper提供了根節(jié)點和子節(jié)點這樣一種實現(xiàn)方式浩淘,當客戶機連接到zookeeper時捌朴,在lock/節(jié)點下創(chuàng)建子節(jié)點也就是臨時節(jié)點,并同時設置監(jiān)聽器张抄,當客戶機監(jiān)聽到自己的節(jié)點屬于最小的節(jié)點時就可以認為是獲得了鎖砂蔽,這時候可以執(zhí)行業(yè)務邏輯,執(zhí)行完畢后zookeeper刪除這個臨時節(jié)點
參考博文:
https://blog.51cto.com/13732225/2165988