在并發(fā)環(huán)境下想要共享變量撮奏,一旦涉及修改操作,就需要用到鎖了弯菊。
Java 中的鎖有這么幾種:synchronized
纵势、reentrant lock
、還有reentrant lock 衍生出的其他鎖比如ReadWriteReentrantLock
鎖性能比較:
這幾種鎖在爭(zhēng)用量級(jí)不同的情況下性能是不同的管钳,就synchronized钦铁、ReentrantLock來(lái)分析比較的話,看到網(wǎng)上有好多博客都在說(shuō)sychronized 在爭(zhēng)用頻次非常高的情況下性能會(huì)急劇下降才漆,這種觀點(diǎn)是存在時(shí)效性的牛曹,就當(dāng)前1.8版本使用體驗(yàn)而言,sychronized在大量爭(zhēng)用的情況性能其實(shí)還好并不會(huì)出現(xiàn)所謂的急劇下降醇滥,倒是在激烈爭(zhēng)用時(shí)sychronized的性能要好一些黎比,這個(gè)問(wèn)題去官網(wǎng)確認(rèn)了下,就現(xiàn)狀而言官方是建議使用sychronized的鸳玩,這次的體驗(yàn)也是sychronized更好阅虫。因?yàn)楫?dāng)前JVM是對(duì)于sychronized做出了優(yōu)化了,借鑒ReentrantLock的CAS加鎖方式怀喉,并且引入了偏向鎖书妻、輕量級(jí)鎖等特性后,常規(guī)情況下兩者比較相似躬拢,實(shí)踐中得到的體驗(yàn)是sychronized性能更好一點(diǎn)躲履,可能是JVM層面加鎖之后,并且編譯器同時(shí)做優(yōu)化Sychronized 更適合在用戶態(tài)把加鎖問(wèn)題解決避免陷入內(nèi)核態(tài)的線程阻塞更有用吧聊闯。
鎖粒度:
synchronized 所支持的鎖粒度相較于ReentrantLock來(lái)說(shuō)是處于劣勢(shì)的工猜,但是對(duì)于大多數(shù)場(chǎng)景來(lái)說(shuō)synchronized足夠用了。
鎖特性:
synchronized 支持的一些偏向鎖只能說(shuō)是性能上的特性不能算是功能上的菱蔬,但是加鎖方便篷帅,不需要顯示加鎖釋放鎖,不容易產(chǎn)生死鎖拴泌,代碼編寫(xiě)簡(jiǎn)單(編寫(xiě)簡(jiǎn)單這事兒并不是很在意魏身,目的是提升性能)
ReentrantLock 特性就比較豐富了,支持公平鎖蚪腐、鎖超時(shí)箭昵,更大程度上避免了死鎖,但是ReentrantLock 能夠控制的粒度更細(xì)回季,并且衍生出來(lái)的工具十分好用家制,比如說(shuō)讀寫(xiě)鎖正林,但是也產(chǎn)生了需要手動(dòng)釋放鎖這個(gè)問(wèn)題。
其他鎖相關(guān)的基礎(chǔ)知識(shí)颤殴,可以看一下我博客Java Concurrent系列的文章觅廓,這里就不過(guò)多贅述了。
這里重點(diǎn)要說(shuō)的是使用鎖的一些方式:
1涵但、鎖選擇
鑒于上面性能比較的結(jié)果杈绸,推薦使用sychronized
2、鎖粒度
粒度要盡可能的控制到小矮瘟,避免不必要的加鎖蝇棉。因?yàn)橥綁K越長(zhǎng),線程持有鎖的時(shí)間就越長(zhǎng)芥永,其他線程等待的時(shí)間就越長(zhǎng),如果整個(gè)都是加鎖的钝吮,那么整個(gè)程序就變成串行處理了埋涧。
3、避免加鎖
一些能夠犧牲空間來(lái)進(jìn)行ThreadLocal處理的奇瘦,就沒(méi)必要使用鎖了棘催,加鎖完全是為了并發(fā)下邏輯的正確,如果有更好的解決方式耳标,請(qǐng)避免使用鎖醇坝,但是如果像是一些非得使用鎖的情況,也務(wù)必主要鎖的粒度次坡,像是直接給函數(shù)加鎖呼猪,實(shí)在是不應(yīng)該。
4砸琅、減少部分加鎖
比如限流計(jì)數(shù)器宋距,我們需要先判定是否大于0再?zèng)Q定是否進(jìn)行減一操作,這是經(jīng)典的競(jìng)態(tài)條件症脂,按理說(shuō)應(yīng)該是加鎖的谚赎,但是如果一共就200個(gè)線程爭(zhēng)用,我們就可以合理的控制了诱篷,count 初始值為1000壶唤,假設(shè)每個(gè)線程只發(fā)起一次請(qǐng)求,我們要保證的是count不會(huì)減到0以下棕所,我們前800次其實(shí)是沒(méi)必要進(jìn)行同步處理的闸盔,只需要用下原子類就可以了。之后后面100多次再產(chǎn)生同步就好了橙凳。
5蕾殴、相關(guān)并發(fā)工具的選擇
在高qps下使用Concurrent 包下的工具時(shí)笑撞,一定要先知道原理或者看看源碼再使用,切不可盲目使用因?yàn)楹芏喙ぞ咭恍┨匦允菦](méi)有用的但是為了這些特性增加了很多額外的加鎖操作钓觉。然后也要知道如果有剛好符合你的需求茴肥,一定要用API 這樣才能得到更好的性能及更大的正確性保證。