1)、排他性:同一時間只會有一個客戶端能獲取到鎖,其餘客戶端同時不能獲取;java
2)、避免死鎖:鎖在一段時間後必定會被釋放;web
3)、高可用:獲取或釋放鎖的機制必須高可用且性能佳。redis
實現方式:
a、基於數據庫實現 基於數據庫的樂觀鎖:在數據庫表中引入一個版本號字段。在修改數據的時候,版本號同時改變。另外一我的去修改的同時,對比版本號,不一致,則操做不成功。則從新操做。 基於數據庫的悲觀鎖:MySQL中基於for update實現,排它鎖;算法
b、基於Redis實現 主要是依賴Redis自身的原子操做:set user_key user_value NX PX 100 ,解鎖就是刪除該key。 當key不存在纔會執行成功,當多個進程同時併發設置一個key,只有一個進程成功。 針對redis集羣模式的分佈式鎖,能夠採用redis的redlock機制;spring
c、基於ZooKeeper實現 使用臨時有序節點來實現分佈式鎖 具體實現還得看其餘文章,這只是個大概講解。數據庫
在加鎖過程,線程1運行,線程2處於等待隊列。 線程1運行結束,結果線程3搶先於線程2進行。這就是非公平鎖的簡單含義。架構
在ReentrantLock lock = new ReentrantLock()默認的就是非公平鎖,構造函數中加入true:ReentrantLock lock = new ReentrantLock(true)。併發
公平鎖原則就是線程3就緒後,會先查看等待隊列是否有線程,有就排隊。框架
Redis鎖引入Redisson(一個開源框架)的依賴異步
代碼: Rlock lock = redisson.getLocl(」myLock「): 加鎖:lock.lock(); 解鎖lock.unlock();
實現原理:
1)、加鎖機制:若是某個客戶端要加鎖,面對的是redis集羣,首先會根據hash幾點選擇一臺機器。而後發送一段lua腳本到redis上,腳本中的KEYS[1]表明加鎖的key,ARGV[1]表明鎖默認生存時間30秒,ARGV[2]表明客戶端ID;
2)、鎖互斥機制:當客戶端1訪問,加鎖。客戶端2訪問,key已經存在,會獲得一個鎖的剩餘時間。而後進入一個while循環,不短嘗試加鎖。等客戶端1鎖釋放,客戶端2就會加鎖;
3)、watch dog 自動延期機制:加鎖時間超過30秒,客戶端1還想繼續持有這把鎖,那就啓動watch dog看門狗,是一個後臺線程,每隔10秒檢查1下,若是客戶端1還持有鎖,就會不斷延長鎖key的生存時間;
4)、可重入加鎖機制:若是客戶端1都已經持有這把鎖,結果可重入的加鎖。此時就會對客戶端1的加鎖次數+1;
5)、釋放鎖機制:lock.unlock()是對鎖數據的加鎖次數-1,發現是0的時候即客戶端1再也不持有這把鎖。客戶端2此時就能夠加鎖了。
在集羣狀況下,機器A申請到了鎖L,結果A宕機,並無同步到集羣的其餘機器上,可能機器B也會申請到鎖L。 因此提出了紅鎖(RedLock)的算法:
1)、首先生成集羣中多個機器的RLock,並將其構建成RedLock;
2)、一次對三個集羣加鎖,過程和普通Redis加鎖一致;
3)、若是循環加鎖失敗,判斷失敗數是否小於集羣數N/2,必須保證失敗數小於集羣數N/2;
4)、另外鎖超時是全部的鎖時間相加,若是超時時間5ms,集羣1已經5ms,則加鎖失敗;
5)、三、4步失敗的話,就會進行解鎖操做。
缺點:
集羣狀況下,對某個redis master實例,寫入myLock這種鎖key的value,會異步複製給對應的master slave。當master宕機,主備切換,redis slave變成redis master,會致使客戶端2來加鎖的時候在新的客戶端上完成加鎖(加鎖針對1臺機器),客戶端1也進行了加鎖。 就會多個客戶端對一個分佈式鎖進行加鎖,產生髒數據。
多個客戶端(線程)爭搶一個zk分佈式鎖:
1)、因此線程都會建立一個鎖節點下的一個接一個的順序節點,按照請求時間設置一個字段排序;
2)、當訪問到zk的鎖節點時候,本身是第一順序位,就加鎖。 若是本身不是第一順序位,就對上一個順序位的節點加監聽器;
3)、只要監聽到上一個節點釋放了鎖,本身就排序到前一個順序位了。
其實就是一個排隊機制。
參考:純潔的微笑、石杉的架構筆記、java版web項目、架構師之路、springForAll社區等公衆號。