【轉(zhuǎn)】java高并發(fā)鎖的3種實(shí)現(xiàn)
標(biāo)簽:JAVA發(fā)布于 2017-06-01 09:59:50
初級(jí)技巧 - 樂(lè)觀鎖
樂(lè)觀鎖適合這樣的場(chǎng)景:讀不會(huì)沖突孝情,寫會(huì)沖突。同時(shí)讀的頻率遠(yuǎn)大于寫李破。
以下面的代碼為例,悲觀鎖的實(shí)現(xiàn):
publicObject?get(Object?key)?{
synchronized(map)?{
if(map.get(key)?==null)?{
//?set?some?values
}
returnmap.get(key);
}
}
樂(lè)觀鎖的實(shí)現(xiàn):
publicObject?get(Object?key)?{
Object?val?=null;
if((val?=?map.get(key)?==null)?{
//?當(dāng)map取值為null時(shí)再加鎖判斷
synchronized(map)?{
if(val?=?map.get(key)?==null)?{
//?set?some?value?to?map...
}
}
}
returnmap.get(key);
}
中級(jí)技巧 - String.intern()
樂(lè)觀鎖不能很好解決大量寫沖突問(wèn)題,但是如果很多場(chǎng)景下吭产,鎖實(shí)際上只是針對(duì)某個(gè)用戶或者某個(gè)訂單个从。比如一個(gè)用戶必須先創(chuàng)建session脉幢,才能進(jìn)行后面的操作。但是由于網(wǎng)絡(luò)原因嗦锐,創(chuàng)建用戶session的請(qǐng)求和后續(xù)請(qǐng)求幾乎同時(shí)達(dá)到嫌松,而并行線程可能會(huì)先處理后續(xù)請(qǐng)求。一般情況奕污,需要對(duì)用戶sessionMap加鎖萎羔,比如上面的樂(lè)觀鎖。在這種場(chǎng)景下碳默,可以講鎖限定到用戶本身上贾陷,即從原來(lái)的
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);
這個(gè)比較類似于數(shù)據(jù)庫(kù)表鎖和行鎖的概念,顯然行鎖的并發(fā)能力比表鎖高很多嘱根。
使用String.inter()是這種思路的一種具體實(shí)現(xiàn)髓废。類 String 維護(hù)一個(gè)字符串池。 當(dāng)調(diào)用 intern 方法時(shí)该抒,如果池已經(jīng)包含一個(gè)等于此 String 對(duì)象的字符串(該對(duì)象由 equals(Object) 方法確定)慌洪,則返回池中的字符串∪岜疲可見(jiàn)蒋譬,當(dāng)String相同時(shí),String.intern()總是返回同一個(gè)對(duì)象愉适,因此就實(shí)現(xiàn)了對(duì)同一用戶加鎖犯助。由于鎖的粒度局限于具體用戶,使系統(tǒng)獲得了最大程度的并發(fā)维咸。
publicvoiddoSomeThing(String?uid)?{
synchronized(uid.intern())?{
//?...
}
}
CopyOnWriteMap剂买?
既然說(shuō)到了“類似于數(shù)據(jù)庫(kù)中的行鎖的概念”惠爽,就不得不提一下MVCC,Java中CopyOnWrite類實(shí)現(xiàn)了MVCC瞬哼。Copy On Write是這樣一種機(jī)制婚肆。當(dāng)我們讀取共享數(shù)據(jù)的時(shí)候,直接讀取坐慰,不需要同步较性。當(dāng)我們修改數(shù)據(jù)的時(shí)候,我們就把當(dāng)前數(shù)據(jù)Copy一份副本结胀,然后在這個(gè)副本 上進(jìn)行修改赞咙,完成之后,再用修改后的副本糟港,替換掉原來(lái)的數(shù)據(jù)攀操。這種方法就叫做Copy On Write。
但是秸抚,速和,,JDK并沒(méi)有提供CopyOnWriteMap剥汤,為什么颠放?下面有個(gè)很好的回答,那就是已經(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.
高級(jí)技巧 - 類ConcurrentHashMap
String.inter()的缺陷是類 String 維護(hù)一個(gè)字符串池是放在JVM perm區(qū)的若贮,如果用戶數(shù)特別多省有,導(dǎo)致放入字符串池的String不可控,有可能導(dǎo)致OOM錯(cuò)誤或者過(guò)多的Full GC谴麦。怎么樣能控制鎖的個(gè)數(shù)蠢沿,同時(shí)減小粒度鎖呢?直接使用Java ConcurrentHashMap匾效?或者你想加入自己更精細(xì)的控制舷蟀?那么可以借鑒ConcurrentHashMap的方式,將需要加鎖的對(duì)象分為多個(gè)bucket面哼,每個(gè)bucket加一個(gè)鎖野宜,偽代碼如下:
Map?locks?=newMap();
List?lockKeys?=newList();
for(intnumber?:1-10000)?{
Object?lockKey?=newObject();
lockKeys.add(lockKey);
locks.put(lockKey,newObject());
}
publicvoiddoSomeThing(String?uid)?{
Object?lockKey?=?lockKeys.get(uid.hash()?%?lockKeys.size());
Object?lock?=?locks.get(lockKey);
synchronized(lock)?{
//?do?something
}
}
原文請(qǐng)參考:http://www.roncoo.com/article/detail/128506