Java中的锁的概念大汇总
文章目錄
- 公平鎖/非公平鎖
- 公平鎖
- 非公平鎖
- 樂觀鎖/悲觀鎖
- 樂觀鎖
- 悲觀鎖
- 獨(dú)占鎖/共享鎖
- 獨(dú)占鎖(排它鎖)
- 共享鎖
- 互斥鎖/讀寫鎖
- 互斥鎖
- 讀寫鎖
- 偏向鎖/輕量級鎖/重量級鎖
- 偏向鎖
- 輕量級鎖
- 重量級鎖
- 可重入鎖
- 分段鎖
- 自旋鎖
- 鎖粗化
- 鎖消除
公平鎖/非公平鎖
公平鎖
公平鎖指的是多線程環(huán)境中按照申請鎖的順序獲取鎖,類似FIFO隊(duì)列,先申請鎖的線程在有資源的情況下保證先獲取到鎖。
示例說明一下公平鎖:
打印結(jié)果:
[pool-1-thread-2] 獲取鎖 [pool-1-thread-1] 獲取鎖 [pool-1-thread-4] 獲取鎖 [pool-1-thread-3] 獲取鎖 [pool-1-thread-5] 獲取鎖 [pool-1-thread-2] 釋放鎖 [pool-1-thread-1] 釋放鎖 [pool-1-thread-4] 釋放鎖 [pool-1-thread-3] 釋放鎖 [pool-1-thread-5] 釋放鎖可以看出來這里獲取鎖和釋放鎖的順序是按照申請鎖的順序來的,這就是公平鎖。 new ReentrantLock(false)是一種公平鎖。
非公平鎖
非公平鎖對應(yīng)的是公平鎖,在多線程環(huán)境中獲取鎖的順序并不是按申請鎖的順序來進(jìn)行的。
測試代碼跟上面的一樣,只需要改一處地方:
打印結(jié)果:
[pool-1-thread-1] 獲取鎖 [pool-1-thread-2] 獲取鎖 [pool-1-thread-3] 獲取鎖 [pool-1-thread-4] 獲取鎖 [pool-1-thread-5] 獲取鎖 [pool-1-thread-2] 釋放鎖 [pool-1-thread-3] 釋放鎖 [pool-1-thread-4] 釋放鎖 [pool-1-thread-1] 釋放鎖 [pool-1-thread-5] 釋放鎖可以看出,獲取鎖的順序并沒有按照申請鎖的順序來走,其實(shí)是隨機(jī)的。非公平鎖可以增加業(yè)務(wù)的吞吐量,因?yàn)槭请S機(jī)讓獲取到資源的線程執(zhí)行的,但是有可能造成優(yōu)先級反轉(zhuǎn)或者線程饑餓問題(可能存在有個(gè)倒霉的線程一直獲取不到鎖,一直在等待),因此是不公平的鎖。
樂觀鎖/悲觀鎖
樂觀鎖和悲觀鎖更像是某種思想,就像是看待問題的方式,樂觀鎖傾向于樂觀的方式看待數(shù)據(jù)認(rèn)為每次獲取的只有一個(gè)線程,悲觀鎖傾向于悲觀的方式看待數(shù)據(jù)認(rèn)為每次獲取都是多個(gè)線程,所以需要將數(shù)據(jù)鎖定只讓一個(gè)線程訪問。
樂觀鎖
樂觀鎖總是認(rèn)為不存在多線程并發(fā)問題,因此每次獲取數(shù)據(jù)的時(shí)候總認(rèn)為不會有其他線程對數(shù)據(jù)進(jìn)行修改,因此不會上鎖。但是更新的時(shí)候會進(jìn)行數(shù)據(jù)的比較以防止被其他線程已經(jīng)修改。實(shí)現(xiàn)方式一般使用“數(shù)據(jù)版本機(jī)制”或者“CAS操作”來實(shí)現(xiàn)。
在Java中java.util.concurrent.atomic包下面的原子變量類就是使用了樂觀鎖的一種實(shí)現(xiàn)方式CAS(Compare and Swap 比較并交換)實(shí)現(xiàn)的。
悲觀鎖
悲觀鎖總是認(rèn)為數(shù)據(jù)總是多線程訪問的,如果對數(shù)據(jù)進(jìn)行操作必須加鎖實(shí)現(xiàn)。對數(shù)據(jù)操作之前加鎖,操作完成或者操作異常都要解鎖。
悲觀鎖有synchronized、ReentrantLock等。
獨(dú)占鎖/共享鎖
獨(dú)占鎖(排它鎖)
獨(dú)占鎖指的是鎖資源被一個(gè)線程所持有,每一次只能一個(gè)線程獨(dú)占使用。ReentrantLock和 synchronized都是獨(dú)占鎖。
共享鎖
共享鎖指的是鎖資源可以被多個(gè)線程持有的。如果線程A對數(shù)據(jù)Data加共享鎖后,其他線程只能對Data再加共享鎖,不能加獨(dú)占鎖。獨(dú)占鎖和共享鎖在JAVA中都是通過AQS實(shí)現(xiàn)的。 ReentrantReadWriteLock讀鎖是共享鎖,寫鎖是獨(dú)占鎖。讀鎖的共享可以保證并發(fā)讀是高效的,讀寫,寫讀,寫寫是互斥的。
互斥鎖/讀寫鎖
互斥鎖
互斥鎖指的最多只能有一個(gè)線程持有的鎖,屬于獨(dú)占鎖的一種。ReentrantLock和 synchronized都是互斥鎖。
讀寫鎖
讀寫鎖指的具體的關(guān)于讀寫的一種鎖,既有獨(dú)占鎖又有共享鎖,read模式就是共享的,但是write模式是獨(dú)占的。
讀寫鎖的機(jī)制:
java中讀寫鎖的實(shí)現(xiàn)是ReentrantReadWriteLock。
偏向鎖/輕量級鎖/重量級鎖
這三種鎖是指鎖的狀態(tài),并且是針對Synchronized。在Java 5通過引入鎖升級的機(jī)制來實(shí)現(xiàn)高效Synchronized。這三種鎖的狀態(tài)是通過對象監(jiān)視器在對象頭中的字段來表明的。
JVM通過以下方式關(guān)閉偏向鎖:
偏向鎖
偏向鎖是指一段同步代碼一直被一個(gè)線程所訪問,那么該線程會自動獲取鎖。降低獲取鎖的代價(jià)。
輕量級鎖
輕量級鎖是指當(dāng)鎖是偏向鎖的時(shí)候,被另一個(gè)線程所訪問,偏向鎖就會升級為輕量級鎖,其他線程會通過自旋的形式嘗試獲取鎖,不會阻塞,提高性能。
重量級鎖
重量級鎖是指當(dāng)鎖為輕量級鎖的時(shí)候,另一個(gè)線程雖然是自旋,但自旋不會一直持續(xù)下去,當(dāng)自旋一定次數(shù)的時(shí)候,還沒有獲取到鎖,就會進(jìn)入阻塞,該鎖膨脹為重量級鎖。重量級鎖會讓他申請的線程進(jìn)入阻塞,性能降低。
可重入鎖
可重入的意思其實(shí)就是可重復(fù)使用,就是一把鎖可以重復(fù)多次用,但是注意需要按照獲取鎖的次數(shù)進(jìn)行解鎖,也就是加鎖此時(shí)一定要等于解鎖次數(shù)。ReentrantLock和 synchronized都是可重入鎖。
分段鎖
分段鎖指的是一種設(shè)計(jì),并非真正的鎖。對于ConcurrentHashMap而言,其并發(fā)的實(shí)現(xiàn)就是通過分段鎖的形式來實(shí)現(xiàn)高效的并發(fā)操作。分段鎖的設(shè)計(jì)目的是細(xì)化鎖的粒度,當(dāng)操作不需要更新整個(gè)數(shù)組的時(shí)候,就僅僅針對數(shù)組中的一項(xiàng)進(jìn)行加鎖操作。
自旋鎖
自旋鎖其實(shí)也不是真正的鎖,只是一種現(xiàn)象的描述,原理就是通過循環(huán)去實(shí)現(xiàn)。
例如JDK源碼中很多都是通過for循環(huán)實(shí)現(xiàn)自旋鎖的。
鎖粗化
鎖粗化簡單理解就是將多個(gè)鎖合并成一個(gè),減少重復(fù)鎖請求帶來的性能損耗。一個(gè)例子理解:
public void doSomethingMethod(){while(i<1000){synchronized(lock){//do some thing}} }鎖粗化之后的代碼
public void doSomethingMethod(){synchronized(lock){while(i<1000){//do some thing}} }鎖消除
鎖消除是發(fā)生在編譯器級別的一種鎖優(yōu)化方式。舉個(gè)簡單例子可以理解:
public synchronized void logApend(String content){StringBuffer sb = new StringBuffer();sb.append(content);}代碼非常簡單,就是向StringBuffer中添加內(nèi)容的方法,并且這個(gè)方法是synchronized來修飾的。其實(shí)我們看apppd方法的源碼發(fā)現(xiàn)這里也有一個(gè)synchronized來修飾。
@Overridepublic synchronized StringBuffer append(String str) {toStringCache = null;super.append(str);return this;}我們可以通過編譯器將其優(yōu)化,將鎖消除。
前提是java必須運(yùn)行在server模式(server模式會比client模式作更多的優(yōu)化),同時(shí)必須開啟逃逸分析:
+DoEscapeAnalysis:開啟逃逸分析
+EliminateLocks:鎖消除。
總結(jié)
以上是生活随笔為你收集整理的Java中的锁的概念大汇总的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 吐血总结:AQS到底是什么?
- 下一篇: Java读写文件的几种方式