淺談分佈式鎖

1. 什麼是鎖?

      在單進程的系統中,當存在多個線程能夠同時改變某個變量時,就須要對變量或代碼塊作同步,使其在修改這種變量時可以線性執行消除併發修改變量。
   而同步的本質是經過鎖來實現的。爲了實現多個線程在一個時刻同一個代碼塊只能有一個線程可執行,那麼須要在某個地方作個標記,這個標記必須每一個線程都能看到,當標記不存在時能夠設置該標記,其他後續線程發現已經有標記了則等待擁有標記的線程結束同步代碼塊取消標記後再去嘗試設置標記。這個標記能夠理解爲鎖。
   不一樣地方實現鎖的方式也不同,只要能知足全部線程都能看獲得標記便可。如java中synchronize是在對象頭設置標記,Lock接口的實現類基本上都只是某一個volitile修飾的int型變量其保證每一個線程都能擁有對該int的可見性和原子修改,linux內核中也是利用互斥量或信號量等內存數據作標記。java

2. 什麼是分佈式鎖?

    分佈式鎖是控制分佈式系統之間同步訪問共享資源的一種方式。在分佈式系統中,經常須要協調他們的動做。若是不一樣的系統或是同一個系統的不一樣主機之間共享了一個或一組資源,那麼訪問這些資源的時候,每每須要互斥來防止彼此干擾來保證一致性,在這種狀況下,便須要使用到分佈式鎖。linux

3. 分佈式鎖實現的幾種方式(每種方式都具有多種實現方案)
  • 基於數據庫實現分佈式鎖

基於數據庫樂觀鎖實現:
樂觀鎖一般實現基於數據版本號(version)的記錄機制實現的,在修改數據庫前獲取版本號,修改數據時與獲取版本號不一致則拋出異常(修改數據時切記對版本號進行+1)git

  • 基於緩存(redis...)實現分佈式鎖

使用redis的setnx()、get()、getset()方法,用於分佈式鎖redis

  1. setnx(lockkey, 當前時間+過時超時時間) ,若是返回1,則獲取鎖成功;若是返回0則沒有獲取到鎖,轉向2。
  2. get(lockkey)獲取值oldExpireTime ,並將這個value值與當前的系統時間進行比較,若是小於當前系統時間,則認爲這個鎖已經超時,能夠容許別的請求從新獲取,轉向3。
  3. 計算newExpireTime=當前時間+過時超時時間,而後getset(lockkey, newExpireTime) 會返回當前lockkey的值currentExpireTime。
  4. 判斷currentExpireTime與oldExpireTime 是否相等,若是相等,說明當前getset設置成功,獲取到了鎖。若是不相等,說明這個鎖又被別的請求獲取走了,那麼當前請求能夠直接返回失敗,或者繼續重試。
  5. 在獲取到鎖以後,當前線程能夠開始本身的業務處理,當處理完畢後,比較本身的處理時間和對於鎖設置的超時時間,若是小於鎖設置的超時時間,則直接執行delete釋放鎖;若是大於鎖設置的超時時間,則不須要再鎖進行處理(這樣能夠避免死鎖)。
  • 基於Zookeeper的分佈式鎖

    利用臨時節點與 watch 機制。每一個鎖佔用一個普通節點 /lock,當須要獲取鎖時在 /lock 目錄下建立一個臨時節點,建立成功則表示獲取鎖成功,失敗則 watch/lock 節點,有刪除操做後再去爭鎖。臨時節點好處在於當進程掛掉後能自動上鎖的節點自動刪除即取消鎖spring

4. 推薦開源分佈式鎖lock-spring-boot-starter

    基於redisson實現的spring boot starter分佈式鎖框架,實現了可重入鎖、公平鎖、聯鎖、紅鎖、讀寫鎖等經常使用鎖的方式,並支持集羣模式下的redis。數據庫

爲何推薦此項目?緩存

  1. 我參與項目的開發
  2. 高可用,使用簡單
  3. 支持單機模式,集羣模式,雲託管模式,哨兵模式
  4. 支持參數加鎖,對象屬性上加鎖,支持方法上加鎖

引入maven依賴便可使用併發

<dependency>
     <groupId>io.gitee.tooleek</groupId>
     <artifactId>lock-spring-boot-starter</artifactId>
     <version>1.1.0</version>
 </dependency>
相關文章
相關標籤/搜索