1. 為什么要使用分布式鎖?
- 變量A存在JVM1购城、JVM2、JVM3三個(gè)JVM(進(jìn)程)內(nèi)存中
- 變量A同時(shí)都會(huì)在JVM分配一塊內(nèi)存虐译,三個(gè)請(qǐng)求發(fā)過來同時(shí)對(duì)這個(gè)變量進(jìn)行操作(例如3個(gè)人買書,書的庫存只有2本)吴趴,顯然結(jié)果是不對(duì)的
- 不是同時(shí)發(fā)過來漆诽,三個(gè)請(qǐng)求分別操作三個(gè)不同JVM內(nèi)存區(qū)域的數(shù)據(jù),變量A之間不存在共享,也不具有可見性,處理的結(jié)果也是不對(duì)的
注:這個(gè)變量主要體現(xiàn)在一個(gè)類中有一個(gè)成員變量尿褪,它是一個(gè)有狀態(tài)的對(duì)象
2. 分布式鎖應(yīng)該具備哪些條件地来?
- 在分布式系統(tǒng)環(huán)境下,一個(gè)方法在同一時(shí)間只能被一個(gè)機(jī)器的一個(gè)線程執(zhí)行
- 高可用的獲取與釋放鎖(zookeeper一直都能提供鎖密末,并且釋放鎖)
- 高性能的獲取鎖與釋放鎖(快)
- 具備可重入性特性(可理解為重新進(jìn)入,由多于一個(gè)任務(wù)并發(fā)使用,而不必?fù)?dān)心數(shù)據(jù)錯(cuò)誤)
可重入(reentrant)函數(shù)可以由多于一個(gè)任務(wù)并發(fā)使用楞捂,而不必?fù)?dān)心數(shù)據(jù)錯(cuò)誤。相反趋厉,不可重入(non-reentrant)函數(shù)不能由超過一個(gè)任務(wù)所共享寨闹,除非能確保函數(shù)的互斥(或者使用信號(hào)量,或者在代碼的關(guān)鍵部分禁用中斷)君账》北ぃ可重入函數(shù)可以在任意時(shí)刻被中斷,稍后再繼續(xù)運(yùn)行乡数,不會(huì)丟失數(shù)據(jù)椭蹄。可重入函數(shù)要么使用本地變量净赴,要么在使用全局變量時(shí)保護(hù)自己的數(shù)據(jù)绳矩。
可重入函數(shù):
1.不為連續(xù)的調(diào)用持有靜態(tài)數(shù)據(jù)。
2.不返回指向靜態(tài)數(shù)據(jù)的指針劫侧;所有數(shù)據(jù)都由函數(shù)的調(diào)用者提供埋酬。
3.使用本地?cái)?shù)據(jù)哨啃,或者通過制作全局?jǐn)?shù)據(jù)的本地拷貝來保護(hù)全局?jǐn)?shù)據(jù)。
4.如果必須訪問全局變量写妥,記住利用互斥信號(hào)量來保護(hù)全局變量拳球。
5.絕不調(diào)用任何不可重入函數(shù)。
- 具備鎖失效機(jī)制,防止死鎖
回顧:多線程是如何避免死鎖的?
我們只要破壞產(chǎn)生死鎖的四個(gè)條件中的其中一個(gè)就可以了珍特。
1.破壞互斥條件
這個(gè)條件我們沒有辦法破壞祝峻,因?yàn)槲覀冇面i本來就是想讓他們互斥的(臨界資源需要互斥訪問)。
2.破壞請(qǐng)求與保持條件
一次性申請(qǐng)所有的資源扎筒。
3.破壞不剝奪條件
占用部分資源的線程進(jìn)一步申請(qǐng)其他資源時(shí)莱找,如果申請(qǐng)不到,可以主動(dòng)釋放它占有的資源嗜桌。
4.破壞循環(huán)等待條件
靠按序申請(qǐng)資源來預(yù)防奥溺。按某一順序申請(qǐng)資源,釋放資源則反序釋放骨宠。破壞循環(huán)等待條件浮定。
我們對(duì)線程 2 的代碼修改成下面這樣就不會(huì)產(chǎn)生死鎖了
- 具備非阻塞鎖特性,即沒有獲取到鎖將直接返回獲取鎖失敗
說明:類似于熔斷機(jī)制,避免大量申請(qǐng)鎖等待而導(dǎo)致的阻塞.
什么是Zookeeper
Zookeeper是一個(gè)為分布式應(yīng)用提供一致性服務(wù)的開源組件,它內(nèi)部是一個(gè)分層的文件系統(tǒng)目錄樹結(jié)構(gòu),規(guī)定同一個(gè)目錄下只能有一個(gè)唯一的文件名,即同一目錄下文件名不能重復(fù)
Zookeeper實(shí)現(xiàn)分布式鎖的步驟
- 創(chuàng)建一個(gè)目錄MyZookeeper
- 線程A想要獲取鎖就在MyZookeeper目錄下創(chuàng)建一個(gè)臨時(shí)順序節(jié)點(diǎn)
- 線程A獲取MyZookeeper目錄下所有的子節(jié)點(diǎn),然后獲取比自己小的兄弟節(jié)點(diǎn),如果不存在,則說明當(dāng)前線程順序號(hào)最小,獲得鎖
- 此時(shí)線程B也獲取鎖,它會(huì)先獲取所有的MyZookeeper節(jié)點(diǎn)下的子節(jié)點(diǎn),如果判斷自己不是最小的節(jié)點(diǎn),就設(shè)置監(jiān)聽比自己次小的節(jié)點(diǎn)
- 線程A處理完畢,刪除自己的節(jié)點(diǎn),線程B監(jiān)聽到變更事件(加入線程B次小的節(jié)點(diǎn)是A),還是判斷下自己是不是最小的節(jié)點(diǎn),如果是就獲得鎖