java高併發鎖的3種實現

初級技巧 - 樂觀鎖

樂觀鎖適合這樣的場景:讀不會衝突,寫會衝突。同時讀的頻率遠大於寫。java

如下面的代碼爲例,悲觀鎖的實現:數據庫

Java代碼  收藏代碼網絡

public Object get(Object key) {  

   synchronized(map) {  

      if(map.get(key) == null) {  

         // set some values  

      }  

  

       return map.get(key);  

   }  

}  

 

樂觀鎖的實現:session

Java代碼  收藏代碼架構

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);

這個比較相似於數據庫表鎖和行鎖的概念,顯然行鎖的併發能力比表鎖高不少。ui

使用String.inter()是這種思路的一種具體實現。類 String 維護一個字符串池。 當調用 intern 方法時,若是池已經包含一個等於此 String 對象的字符串(該對象由 equals(Object) 方法肯定),則返回池中的字符串。可見,當String相同時,String.intern()老是返回同一個對象,所以就實現了對同一用戶加鎖。因爲鎖的粒度侷限於具體用戶,使系統得到了最大程度的併發。this

Java代碼  收藏代碼spa

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加一個鎖,僞代碼以下:

Java代碼  收藏代碼

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  

   }  

}  
  1. Java高併發高可用架構:632103578

相關文章
相關標籤/搜索