如何在java項目中實現(xiàn)一個高并發(fā)鎖?相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
成都創(chuàng)新互聯(lián)公司2013年成立,是專業(yè)互聯(lián)網(wǎng)技術服務公司,擁有項目成都做網(wǎng)站、成都網(wǎng)站設計網(wǎng)站策劃,項目實施與項目整合能力。我們以讓每一個夢想脫穎而出為使命,1280元永安做網(wǎng)站,已為上家服務,為永安各地企業(yè)和個人服務,聯(lián)系電話:18982081108
樂觀鎖
樂觀鎖適合這樣的場景:讀不會沖突,寫會沖突。同時讀的頻率遠大于寫。
以下面的代碼為例,悲觀鎖的實現(xiàn):
public Object get(Object key) { synchronized(map) { if(map.get(key) == null) { // set some values } return map.get(key); } }
樂觀鎖的實現(xiàn):
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()
樂觀鎖不能很好解決大量寫沖突問題,但是如果很多場景下,鎖實際上只是針對某個用戶或者某個訂單。比如一個用戶必須先創(chuàng)建session,才能進行后面的操作。但是由于網(wǎng)絡原因,創(chuàng)建用戶session的請求和后續(xù)請求幾乎同時達到,而并行線程可能會先處理后續(xù)請求。一般情況,需要對用戶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);
這個比較類似于數(shù)據(jù)庫表鎖和行鎖的概念,顯然行鎖的并發(fā)能力比表鎖高很多。
使用String.inter()是這種思路的一種具體實現(xiàn)。類 String 維護一個字符串池。 當調(diào)用 intern 方法時,如果池已經(jīng)包含一個等于此 String 對象的字符串(該對象由 equals(Object) 方法確定),則返回池中的字符串??梢姡擲tring相同時,String.intern()總是返回同一個對象,因此就實現(xiàn)了對同一用戶加鎖。由于鎖的粒度局限于具體用戶,使系統(tǒng)獲得了最大程度的并發(fā)。
public void doSomeThing(String uid) { synchronized(uid.intern()) { // ... } }
CopyOnWriteMap?
既然說到了“類似于數(shù)據(jù)庫中的行鎖的概念”,就不得不提一下MVCC,Java中CopyOnWrite類實現(xiàn)了MVCC。Copy On Write是這樣一種機制。當我們讀取共享數(shù)據(jù)的時候,直接讀取,不需要同步。當我們修改數(shù)據(jù)的時候,我們就把當前數(shù)據(jù)Copy一份副本,然后在這個副本 上進行修改,完成之后,再用修改后的副本,替換掉原來的數(shù)據(jù)。這種方法就叫做Copy On Write。
但是,,,JDK并沒有提供CopyOnWriteMap,為什么?下面有個很好的回答,那就是已經(jīng)有了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區(qū)的,如果用戶數(shù)特別多,導致放入字符串池的String不可控,有可能導致OOM錯誤或者過多的Full GC。怎么樣能控制鎖的個數(shù),同時減小粒度鎖呢?直接使用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 } }
看完上述內(nèi)容,你們掌握如何在java項目中實現(xiàn)一個高并發(fā)鎖的方法了嗎?如果還想學到更多技能或想了解更多相關內(nèi)容,歡迎關注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!