java高并發鎖的3種實現

提到鎖,大家可能都會想到synchronized關鍵字,使用它的確可以解決一切并發問題,但是對于系統吞吐要求更高的,在這里提供了幾個小技巧,幫助大家減小鎖粒度,提高系統并發能力。

初級技巧 - 樂觀鎖
樂觀鎖適合這樣的場景:讀不會沖突,寫會沖突。同時讀的頻率遠大于寫。

以下面的代碼為例,悲觀鎖的實現:

public Object get(Object key) {  
  synchronized(map) {  
     if(map.get(key) == null) {  
        // set some values  
     }  
 
      return map.get(key);  
  }  
}  

樂觀鎖的實現:

public  Object get(Object key) {  
  Object val = null;  
  if((val = map.get(key) == null) {  
      // 當map取值為null時再加鎖判斷  
      synchronized (map) {  
          if(val = map.get(key) == **null**) {  
              // set some value to map...  
          }  
       }  
  }  
 
   return  map.get(key);  
}  

中級技巧 - String.intern()
樂觀鎖不能很好解決大量寫沖突問題,但是如果很多場景下,鎖實際上只是針對某個用戶或者某個訂單。比如一個用戶必須先創建session,才能進行后面的操作。但是由于網絡原因,創建用戶session的請求和后續請求幾乎同時達到,而并行線程可能會先處理后續請求。一般情況,需要對用戶sessionMap加鎖,比如上面的樂觀鎖。在這種場景下,可以講鎖限定到用戶本身上,即從原來的

lock.lock();
   int num=storage.get(key);
   storage.set(key,num+1);
lock.unlock();
更改為:
lock.lock(key);
   int num=storage.get(key);
   storage.set(key,num+1);
lock.unlock(key);

這個比較類似于數據庫表鎖和行鎖的概念,顯然行鎖的并發能力比表鎖高很多。

使用String.inter()是這種思路的一種具體實現。類 String 維護一個字符串池。 當調用 intern 方法時,如果池已經包含一個等于此 String 對象的字符串(該對象由 equals(Object) 方法確定),則返回池中的字符串。可見,當String相同時,String.intern()總是返回同一個對象,因此就實現了對同一用戶加鎖。由于鎖的粒度局限于具體用戶,使系統獲得了最大程度的并發。

public      void doSomeThing(String uid) {  
   synchronized  (uid.intern()) {  
       // ...  
   }  
}  

CopyOnWriteMap?
既然說到了“類似于數據庫中的行鎖的概念”,就不得不提一下MVCC,Java中CopyOnWrite類實現了MVCC。Copy On Write是這樣一種機制。當我們讀取共享數據的時候,直接讀取,不需要同步。當我們修改數據的時候,我們就把當前數據Copy一份副本,然后在這個副本 上進行修改,完成之后,再用修改后的副本,替換掉原來的數據。這種方法就叫做Copy On Write。
但是,,,JDK并沒有提供CopyOnWriteMap,為什么?下面有個很好的回答,那就是已經有了ConcurrentHashMap,為什么還需要CopyOnWriteMap?

Fredrik Bromee 寫道
I guess this depends on your use case, but why would you need a CopyOnWriteMap when you already have a ConcurrentHashMap?For a plain lookup table with many readers and only one or few updates it is a good fit.Compared to a copy on write collection:Read concurrency:Equal to a copy on write collection. Several readers can retrieve elements from the map concurrently in a lock-free fashion.Write concurrency:Better concurrency than the copy on write collections that basically serialize updates (one update at a time). Using a concurrent hash map you have a good chance of doing several updates concurrently. If your hash keys are evenly distributed.If you do want to have the effect of a copy on write map, you can always initialize a ConcurrentHashMap with a concurrency level of 1.

高級技巧 - 類ConcurrentHashMap

String.inter()的缺陷是類 String 維護一個字符串池是放在JVM perm區的,如果用戶數特別多,導致放入字符串池的String不可控,有可能導致OOM錯誤或者過多的Full GC。怎么樣能控制鎖的個數,同時減小粒度鎖呢?直接使用Java ConcurrentHashMap?或者你想加入自己更精細的控制?那么可以借鑒ConcurrentHashMap的方式,將需要加鎖的對象分為多個bucket,每個bucket加一個鎖,偽代碼如下:

Map locks = new Map();  
List lockKeys =  new List();  
 for ( int  number : 1 - 10000) {  
   Object lockKey =  new  Object();  
   lockKeys.add(lockKey);  
    locks.put(lockKey, new Object());  
}  
public  void doSomeThing(String uid) {  
   Object lockKey = lockKeys.get(uid.hash() % lockKeys.size());  
   Object lock = locks.get(lockKey);  
     
   synchronized (lock) {  
      // do something  
   }  
}  
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 【轉】java高并發鎖的3種實現 標簽:JAVA發布于 2017-06-01 09:59:50 初級技巧 - 樂觀...
    shine9609閱讀 723評論 0 2
  • 個人筆記,方便自己查閱使用 Contents Java LangAssignment, ReferenceData...
    freenik閱讀 1,411評論 0 6
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,923評論 18 139
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,765評論 18 399
  • 五六章從船長這里學習到了“變現”、“轉化率”兩部分。 變現這部分從變現的理解、變現的時間、變現的產品誤區、三種主流...
    喵星人cici閱讀 281評論 0 0