您好,登錄后才能下訂單哦!
這篇文章主要介紹了在Java并發(fā)場(chǎng)景中有哪些鎖,具有一定借鑒價(jià)值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。
01.樂觀鎖 vs 悲觀鎖
樂觀鎖與悲觀鎖是一種廣義上的概念,體現(xiàn)了看待線程同步的不同角度,在Java和數(shù)據(jù)庫(kù)中都有此概念對(duì)應(yīng)的實(shí)際應(yīng)用。
1.樂觀鎖
顧名思義,就是很樂觀,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人不會(huì)修改,所以不會(huì)上鎖,但是在更新的時(shí)候會(huì)判斷一下在此期間別人有沒有去更新這個(gè)數(shù)據(jù),可以使用版本號(hào)等機(jī)制。
樂觀鎖適用于多讀的應(yīng)用類型,樂觀鎖在Java中是通過使用無鎖編程來實(shí)現(xiàn),最常采用的是CAS算法,Java原子類中的遞增操作就通過CAS自旋實(shí)現(xiàn)的。
CAS全稱 Compare And Swap(比較與交換),是一種無鎖算法。在不使用鎖(沒有線程被阻塞)的情況下實(shí)現(xiàn)多線程之間的變量同步。java.util.concurrent包中的原子類就是通過CAS來實(shí)現(xiàn)了樂觀鎖。
簡(jiǎn)單來說,CAS算法有3個(gè)三個(gè)操作數(shù):
需要讀寫的內(nèi)存值 V。
進(jìn)行比較的值 A。
要寫入的新值 B。
當(dāng)且僅當(dāng)預(yù)期值A(chǔ)和內(nèi)存值V相同時(shí),將內(nèi)存值V修改為B,否則返回V。這是一種樂觀鎖的思路,它相信在它修改之前,沒有其它線程去修改它;而Synchronized是一種悲觀鎖,它認(rèn)為在它修改之前,一定會(huì)有其它線程去修改它,悲觀鎖效率很低。
2.悲觀鎖
總是假設(shè)最壞的情況,每次去拿數(shù)據(jù)的時(shí)候都認(rèn)為別人會(huì)修改,所以每次在拿數(shù)據(jù)的時(shí)候都會(huì)上鎖,這樣別人想拿這個(gè)數(shù)據(jù)就會(huì)阻塞直到它拿到鎖。
傳統(tǒng)的MySQL關(guān)系型數(shù)據(jù)庫(kù)里邊就用到了很多這種鎖機(jī)制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。詳情可以參考:阿里P8架構(gòu)師談:MySQL行鎖、表鎖、悲觀鎖、樂觀鎖的特點(diǎn)與應(yīng)用
再比如上面提到的Java的同步synchronized關(guān)鍵字的實(shí)現(xiàn)就是典型的悲觀鎖。
3.總之:
悲觀鎖適合寫操作多的場(chǎng)景,先加鎖可以保證寫操作時(shí)數(shù)據(jù)正確。
樂觀鎖適合讀操作多的場(chǎng)景,不加鎖的特點(diǎn)能夠使其讀操作的性能大幅提升。
02.公平鎖 vs 非公平鎖
1.公平鎖
就是很公平,在并發(fā)環(huán)境中,每個(gè)線程在獲取鎖時(shí)會(huì)先查看此鎖維護(hù)的等待隊(duì)列,如果為空,或者當(dāng)前線程是等待隊(duì)列的***個(gè),就占有鎖,否則就會(huì)加入到等待隊(duì)列中,以后會(huì)按照FIFO的規(guī)則從隊(duì)列中取到自己。
公平鎖的優(yōu)點(diǎn)是等待鎖的線程不會(huì)餓死。缺點(diǎn)是整體吞吐效率相對(duì)非公平鎖要低,等待隊(duì)列中除***個(gè)線程以外的所有線程都會(huì)阻塞,CPU喚醒阻塞線程的開銷比非公平鎖大。
2.非公平鎖
上來就直接嘗試占有鎖,如果嘗試失敗,就再采用類似公平鎖那種方式。
非公平鎖的優(yōu)點(diǎn)是可以減少喚起線程的開銷,整體的吞吐效率高,因?yàn)榫€程有幾率不阻塞直接獲得鎖,CPU不必喚醒所有線程。缺點(diǎn)是處于等待隊(duì)列中的線程可能會(huì)餓死,或者等很久才會(huì)獲得鎖。
3.典型應(yīng)用:
java jdk并發(fā)包中的ReentrantLock可以指定構(gòu)造函數(shù)的boolean類型來創(chuàng)建公平鎖和非公平鎖(默認(rèn)),比如:公平鎖可以使用new ReentrantLock(true)實(shí)現(xiàn)。
03.獨(dú)享所 vs 共享鎖
1.獨(dú)享鎖
是指該鎖一次只能被一個(gè)線程所持有。
2.共享鎖
是指該鎖可被多個(gè)線程所持有。
3.比較
對(duì)于Java ReentrantLock而言,其是獨(dú)享鎖。但是對(duì)于Lock的另一個(gè)實(shí)現(xiàn)類ReadWriteLock,其讀鎖是共享鎖,其寫鎖是獨(dú)享鎖。
讀鎖的共享鎖可保證并發(fā)讀是非常高效的,讀寫,寫讀 ,寫寫的過程是互斥的。
獨(dú)享鎖與共享鎖也是通過AQS來實(shí)現(xiàn)的,通過實(shí)現(xiàn)不同的方法,來實(shí)現(xiàn)獨(dú)享或者共享。
4.AQS
抽象隊(duì)列同步器(AbstractQueuedSynchronizer,簡(jiǎn)稱AQS)是用來構(gòu)建鎖或者其他同步組件的基礎(chǔ)框架,它使用一個(gè)整型的volatile變量(命名為state)來維護(hù)同步狀態(tài),通過內(nèi)置的FIFO隊(duì)列來完成資源獲取線程的排隊(duì)工作。
concurrent包的實(shí)現(xiàn)結(jié)構(gòu)如上圖所示,AQS、非阻塞數(shù)據(jù)結(jié)構(gòu)和原子變量類等基礎(chǔ)類都是基于volatile變量的讀/寫和CAS實(shí)現(xiàn),而像Lock、同步器、阻塞隊(duì)列、Executor和并發(fā)容器等高層類又是基于基礎(chǔ)類實(shí)現(xiàn)。
04.分段鎖
分段鎖其實(shí)是一種鎖的設(shè)計(jì),并不是具體的一種鎖,對(duì)于ConcurrentHashMap而言,其并發(fā)的實(shí)現(xiàn)就是通過分段鎖的形式來實(shí)現(xiàn)高效的并發(fā)操作。
我們以ConcurrentHashMap來說一下分段鎖的含義以及設(shè)計(jì)思想,ConcurrentHashMap中的分段鎖稱為Segment,它即類似于HashMap(JDK7與JDK8中HashMap的實(shí)現(xiàn))的結(jié)構(gòu),即內(nèi)部擁有一個(gè)Entry數(shù)組,數(shù)組中的每個(gè)元素又是一個(gè)鏈表;同時(shí)又是一個(gè)ReentrantLock(Segment繼承了ReentrantLock)。
當(dāng)需要put元素的時(shí)候,并不是對(duì)整個(gè)hashmap進(jìn)行加鎖,而是先通過hashcode來知道他要放在那一個(gè)分段中,然后對(duì)這個(gè)分段進(jìn)行加鎖,所以當(dāng)多線程put的時(shí)候,只要不是放在一個(gè)分段中,就實(shí)現(xiàn)了真正的并行的插入。
但是,在統(tǒng)計(jì)size的時(shí)候,可就是獲取hashmap全局信息的時(shí)候,就需要獲取所有的分段鎖才能統(tǒng)計(jì)。
分段鎖的設(shè)計(jì)目的是細(xì)化鎖的粒度,當(dāng)操作不需要更新整個(gè)數(shù)組的時(shí)候,就僅僅針對(duì)數(shù)組中的一項(xiàng)進(jìn)行加鎖操作。
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享的“在Java并發(fā)場(chǎng)景中有哪些鎖”這篇文章對(duì)大家有幫助,同時(shí)也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識(shí)等著你來學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。