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