首先需要知道非分散式環境下,用鎖可以解決什麼?

多線程環境下,共享資源的線程安全問題!這個時候的共享資源通常是在單機裡面的多線程裏存在競爭,從JAVA內存模型來看,可以通過鎖住對象,鎖住方法,鎖住代碼塊等方式,避免共享資源的競爭!

而在分散式環境中,共享資源所要經過的代碼,方法,都不是在一個JVM裡面,也不是同一個進程!通過鎖方法,代碼塊的方式不能解決共享資源的競爭,需要分散式的鎖!

分散式鎖通常在以下場景中使用:

1,全局ID的生成;

2,全局配置文件的修改;

3,分散式服務中的秒殺問題;

4,分散式環境下的重複提交;

分散式鎖通常有以下實現方式:

1,使用資料庫的唯一主鍵來實現鎖!

2,使用redis的指令:通常使用setnx方法,incr方法等進行實現

3,使用zookeeper:使用api生成臨時節點實現鎖!

最近在持續分享JAVA相關技術,有很多乾貨,敬請關注。。


分散式鎖也是鎖
  • 在單體應用的時候,如果多個線程要訪問共享資源的時候,我們通常線程間加鎖的機制,在某一個時刻,只有一個線程可以對這個資源進行操作,其他線程需要等待鎖的釋放,Java中也有一些處理鎖的機制,比如synchronized。

  • 而到了分散式的環境中,當某個資源可以被多個系統訪問使用到的時候,為了保證大家訪問這個數據是一致性的,那麼就要求再同一個時刻,只能被一個系統使用,這時候線程之間的鎖機制就無法起到作用了,因為分散式環境中,系統是會部署到不同的機器上面的,那麼就需要【分散式鎖】了。

什麼時候需要使用分散式鎖

總結來看,當有多個客戶端需要訪問並操作同一個資源,還需要保持這個資源一致性的時候,就需要使用【分散式鎖】,讓多客戶端互斥的對共享資源進行訪問。

舉個例子來說明一下:

  • 有多個批處理任務,兩臺機器同時處理,如果不加任何控制的話,很有可能同一個批處理被兩臺機器分別處理一遍;如果使用分散式鎖,在領取任務的時候,一個任務只會被一臺機器領到,這樣就不會造成任務的重複執行;
  • 再多思考一些,如果A/B兩臺機器,任務1被A機器領取到進行處理,在處理到一半的時候,A機器掛掉了,那麼這個批處理任務也就無法順利執行了,除非A機器可以恢復。

這時候就可以知道分散式鎖需要做哪些工作了
  • 排他性:在同一時間只會有一個客戶端能獲取到鎖,其它客戶端無法同時獲取;
  • 避免死鎖:鎖在一段時間內有效,超過這個時間後會被釋放(正常釋放或異常釋放);
  • 高可用:獲取或釋放鎖的機制必須高可用且性能佳。

我將持續分享Java開發、架構設計、程序員職業發展等方面的見解,希望能得到你的關注。


作為BAT的Java web開發工程師來回答這個問題。

分散式鎖是目前Java web開發過程中常見的一個情況,接下來,我從場景以及如何實現兩個維度來分析。

使用場景

當你的後端服務是以集羣形式存在的時候,是一定需要分散式鎖的。集羣與分散式不同,而這裡的分散式與分散式鎖也不是同一回事兒。集羣可以指多臺伺服器實現了同樣的需求,比如有三臺Tomcat,都負責查詢模塊;而分散式指多臺伺服器各自不同的功能點,多臺功能的整合對外是一個完整的服務,比如一臺Tomcat負責查詢,一臺負責下單。

說回集羣,當後端集羣要去訪問同一個資源的時候,就需要對該資源加鎖,保證同一時刻只能有一個對象來修改該資源數據,如果不加鎖會導致什麼情況呢?

舉一個例子:

有兩個線程(分別叫T1,T2)做的都是同樣的事情,拿到一個叫做A的資源,然後對其進行+1操作。由於線程之間是不會互相通信的,於是就有可能出現下面這種情況:

T1拿到A,讀入內存,此時A值為T;

T2拿到A,讀入內存,此時A值為T;

T1進行+1操作,此時A實際值為T+1;

T2進行+1操作,此時A的實際值仍然為T+1;

然而,此時A經過兩個線程執行+1操作,應該為T+2才對的,所以可以看出,如果沒有分散式鎖,就會出現數據不一致的問題。如果是上面這種簡單的計算還好,如果是你的銀行賬戶,沒用分散式鎖,此時有兩個人給你打錢,結果只有其中一個人的到賬了,另一個人的被作為無主錢財被銀行充公了,肯定是不行的吧。

所以,保障數據一致性和準確性就是分散式鎖的重要性。

如何實現

在這裡可以給大家介紹一個用redis來實現分散式鎖的方案。

Redis對外開放了一個非常厲害的api,目前經常被大家用來做分散式鎖,是絕對的線程安全,這個函數就是SET key field value加上NX參數。這個NX參數可是了不得,通常來說,set函數是不管field欄位是否存在,只要寫入成功就會返回1,但是如果增加了NX參數,那麼如果field值在redis中已經存在,就會返回nil,否則才返回1。因此可以通過這個函數來執行加鎖操作,如果返回值不為nil,則加鎖成功,否則代表有其他線程在操作數據,當前請求需要等待。

不僅如此,為了避免死鎖,SET還有一個參數為EX,即EX毫秒後,field會自動清空。

此外,還有PX,XX參數,具體含義見如下文檔。

以上就是我在工作中總結的防止redis並發的方式,如果你有其他想法,歡迎在下方評論區與我溝通。

我是蘇蘇思量,來自BAT的java開發工程師,每天分享技術見聞,歡迎關注我,與我共同進步。


高並發下爭奪共享資源,比如秒殺對於庫存這種共享資源需要用到分散式鎖,如果不用分散式鎖很可能造成超賣。


推薦閱讀:
相關文章