一、公平鎖/非公平鎖
讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛(ài)。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶,將通過(guò)不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:國(guó)際域名空間、網(wǎng)絡(luò)空間、營(yíng)銷(xiāo)軟件、網(wǎng)站建設(shè)、鎮(zhèn)平網(wǎng)站維護(hù)、網(wǎng)站推廣。
公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖。
非公平鎖是指多個(gè)線程獲取鎖的順序并不是按照申請(qǐng)鎖的順序,有可能后申請(qǐng)的線程比先申請(qǐng)的線程優(yōu)先獲取鎖。有可能,會(huì)造成優(yōu)先級(jí)反轉(zhuǎn)或者饑餓現(xiàn)象。
對(duì)于Java ReentrantLock而言,通過(guò)構(gòu)造函數(shù)指定該鎖是否是公平鎖,默認(rèn)是非公平鎖。非公平鎖的優(yōu)點(diǎn)在于吞吐量比公平鎖大。
對(duì)于Synchronized而言,也是一種非公平鎖。由于其并不像ReentrantLock是通過(guò)AQS的來(lái)實(shí)現(xiàn)線程調(diào)度,所以并沒(méi)有任何辦法使其變成公平鎖。
二、可重入鎖
可重入鎖又名遞歸鎖,是指在同一個(gè)線程在外層方法獲取鎖的時(shí)候,在進(jìn)入內(nèi)層方法會(huì)自動(dòng)獲取鎖。說(shuō)的有點(diǎn)抽象,下面會(huì)有一個(gè)代碼的示例。
對(duì)于Java ReentrantLock而言, 他的名字就可以看出是一個(gè)可重入鎖,其名字是Re entrant Lock重新進(jìn)入鎖。
對(duì)于Synchronized而言,也是一個(gè)可重入鎖??芍厝腈i的一個(gè)好處是可一定程度避免死鎖。
synchronized void setA() throws Exception{
Thread.sleep(1000);
setB();
}
synchronized void setB() throws Exception{
Thread.sleep(1000);
}
上面的代碼就是一個(gè)可重入鎖的一個(gè)特點(diǎn),如果不是可重入鎖的話,setB可能不會(huì)被當(dāng)前線程執(zhí)行,可能造成死鎖。
三、獨(dú)享鎖/共享鎖
獨(dú)享鎖是指該鎖一次只能被一個(gè)線程所持有。
共享鎖是指該鎖可被多個(gè)線程所持有。
對(duì)于Java
ReentrantLock而言,其是獨(dú)享鎖。但是對(duì)于Lock的另一個(gè)實(shí)現(xiàn)類(lèi)ReadWriteLock,其讀鎖是共享鎖,其寫(xiě)鎖是獨(dú)享鎖。
讀鎖的共享鎖可保證并發(fā)讀是非常高效的,讀寫(xiě),寫(xiě)讀 ,寫(xiě)寫(xiě)的過(guò)程是互斥的。
獨(dú)享鎖與共享鎖也是通過(guò)AQS來(lái)實(shí)現(xiàn)的,通過(guò)實(shí)現(xiàn)不同的方法,來(lái)實(shí)現(xiàn)獨(dú)享或者共享。
對(duì)于Synchronized而言,當(dāng)然是獨(dú)享鎖。
四、互斥鎖/讀寫(xiě)鎖
上面講的獨(dú)享鎖/共享鎖就是一種廣義的說(shuō)法,互斥鎖/讀寫(xiě)鎖就是具體的實(shí)現(xiàn)。
互斥鎖在Java中的具體實(shí)現(xiàn)就是ReentrantLock
讀寫(xiě)鎖在Java中的具體實(shí)現(xiàn)就是ReadWriteLock
五、樂(lè)觀鎖/悲觀鎖
樂(lè)觀鎖與悲觀鎖不是指具體的什么類(lèi)型的鎖,而是指看待并發(fā)同步的角度。
悲觀鎖認(rèn)為對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,一定是會(huì)發(fā)生修改的,哪怕沒(méi)有修改,也會(huì)認(rèn)為修改。因此對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,悲觀鎖采取加鎖的形式。悲觀的認(rèn)為,不加鎖的并發(fā)操作一定會(huì)出問(wèn)題。
樂(lè)觀鎖則認(rèn)為對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,是不會(huì)發(fā)生修改的。在更新數(shù)據(jù)的時(shí)候,會(huì)采用嘗試更新,不斷重新的方式更新數(shù)據(jù)。樂(lè)觀的認(rèn)為,不加鎖的并發(fā)操作是沒(méi)有事情的。
從上面的描述我們可以看出,悲觀鎖適合寫(xiě)操作非常多的場(chǎng)景,樂(lè)觀鎖適合讀操作非常多的場(chǎng)景,不加鎖會(huì)帶來(lái)大量的性能提升。
悲觀鎖在Java中的使用,就是利用各種鎖。
樂(lè)觀鎖在Java中的使用,是無(wú)鎖編程,常常采用的是CAS算法,典型的例子就是原子類(lèi),通過(guò)CAS自旋實(shí)現(xiàn)原子操作的更新。
六、分段鎖
分段鎖其實(shí)是一種鎖的設(shè)計(jì),并不是具體的一種鎖,對(duì)于ConcurrentHashMap而言,其并發(fā)的實(shí)現(xiàn)就是通過(guò)分段鎖的形式來(lái)實(shí)現(xiàn)高效的并發(fā)操作。
我們以ConcurrentHashMap來(lái)說(shuō)一下分段鎖的含義以及設(shè)計(jì)思想,ConcurrentHashMap中的分段鎖稱(chēng)為Segment,它即類(lèi)似于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)行加鎖,而是先通過(guò)hashcode來(lái)知道他要放在那一個(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)行加鎖操作。
七、偏向鎖/輕量級(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)表明的。
偏向鎖是指一段同步代碼一直被一個(gè)線程所訪問(wèn),那么該線程會(huì)自動(dòng)獲取鎖。降低獲取鎖的代價(jià)。
輕量級(jí)鎖是指當(dāng)鎖是偏向鎖的時(shí)候,被另一個(gè)線程所訪問(wèn),偏向鎖就會(huì)升級(jí)為輕量級(jí)鎖,其他線程會(huì)通過(guò)自旋的形式嘗試獲取鎖,不會(huì)阻塞,提高性能。
重量級(jí)鎖是指當(dāng)鎖為輕量級(jí)鎖的時(shí)候,另一個(gè)線程雖然是自旋,但自旋不會(huì)一直持續(xù)下去,當(dāng)自旋一定次數(shù)的時(shí)候,還沒(méi)有獲取到鎖,就會(huì)進(jìn)入阻塞,該鎖膨脹為重量級(jí)鎖。重量級(jí)鎖會(huì)讓其他申請(qǐng)的線程進(jìn)入阻塞,性能降低。
八、自旋鎖
在Java中,自旋鎖是指嘗試獲取鎖的線程不會(huì)立即阻塞,而是采用循環(huán)的方式去嘗試獲取鎖,這樣的好處是減少線程上下文切換的消耗,缺點(diǎn)是循環(huán)會(huì)消耗CPU。
典型的自旋鎖實(shí)現(xiàn)的例子,可以參考自旋鎖的實(shí)現(xiàn)
【1】公平所和非公平所。
公平鎖:是指按照申請(qǐng)鎖的順序來(lái)獲取鎖,
非公平所:線程獲取鎖的順序不一定按照申請(qǐng)鎖的順序來(lái)的。
//默認(rèn)是不公平鎖,傳入true為公平鎖,否則為非公平鎖
ReentrantLock reentrantLock = new ReetrantLock();
1
2
【2】共享鎖和獨(dú)享鎖
獨(dú)享鎖:一次只能被一個(gè)線程所訪問(wèn)
共享鎖:線程可以被多個(gè)線程所持有。
ReadWriteLock 讀鎖是共享鎖,寫(xiě)鎖是獨(dú)享鎖。
【3】樂(lè)觀鎖和悲觀鎖。
樂(lè)觀鎖:對(duì)于一個(gè)數(shù)據(jù)的操作并發(fā),是不會(huì)發(fā)生修改的。在更新數(shù)據(jù)的時(shí)候,會(huì)嘗試采用更新,不斷重入的方式,更新數(shù)據(jù)。
悲觀鎖:對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,是一定會(huì)發(fā)生修改的。因此對(duì)于同一個(gè)數(shù)據(jù)的并發(fā)操作,悲觀鎖采用加鎖的形式。悲觀鎖認(rèn)為,不加鎖的操作一定會(huì)出問(wèn)題,
【4】分段鎖
1.7及之前的concurrenthashmap。并發(fā)操作就是分段鎖,其思想就是讓鎖的粒度變小。
【5】偏向鎖是指一段同步代碼一直被一個(gè)線程所訪問(wèn),那么該線程會(huì)自動(dòng)獲取鎖。降低獲取鎖的代價(jià)
輕量級(jí)鎖
重量級(jí)鎖
【6】自旋鎖
自旋鎖
首先Java中的ReentrantLock 默認(rèn)的lock()方法采用的是非公平鎖。
也就是不用考慮其他在排隊(duì)的線程的感受,lock()的時(shí)候直接詢(xún)問(wèn)是否可以獲取鎖,而不用在隊(duì)尾排隊(duì)。
下面分析下公平鎖的具體實(shí)現(xiàn)。
重點(diǎn)關(guān)注java.util.concurrent.locks.AbstractQueuedSynchronizer類(lèi)
幾乎所有l(wèi)ocks包下的工具類(lèi)鎖都包含了該類(lèi)的static子類(lèi),足以可見(jiàn)這個(gè)類(lèi)在java并發(fā)鎖工具類(lèi)當(dāng)中的地位。
這個(gè)類(lèi)提供了對(duì)操作系統(tǒng)層面線程操作方法的封裝調(diào)用,可以幫助并發(fā)設(shè)計(jì)者設(shè)計(jì)出很多優(yōu)秀的API
ReentrantLock當(dāng)中的lock()方法,是通過(guò)static 內(nèi)部類(lèi)sync來(lái)進(jìn)行鎖操作
public void lock()
{
sync.lock();
}
//定義成final型的成員變量,在構(gòu)造方法中進(jìn)行初始化
private final Sync sync;
//無(wú)參數(shù)默認(rèn)非公平鎖
public ReentrantLock()
{
sync = new NonfairSync();
}
//根據(jù)參數(shù)初始化為公平鎖或者非公平鎖
public ReentrantLock(boolean fair)
{
sync = fair ? new FairSync() : new NonfairSync();
}
首先來(lái)看公平鎖和非公平鎖,我們默認(rèn)使用的鎖是非公平鎖,只有當(dāng)我們顯示設(shè)置為公平鎖的情況下,才會(huì)使用公平鎖,下面我們簡(jiǎn)單看一下公平鎖的源碼,如果等待隊(duì)列中沒(méi)有節(jié)點(diǎn)在等待,則占有鎖,如果已經(jīng)存在等待節(jié)點(diǎn),則返回失敗,由后面的程序去將此線程加入等待隊(duì)列
通過(guò)上面的代碼,我們可以推斷,當(dāng)使用公平鎖的情況下,并且同一個(gè)線程的執(zhí)行時(shí)間較長(zhǎng)時(shí),線程內(nèi)部進(jìn)行了多次的鎖的獲取和釋放,效率非常低下,可以參加Lesson8中的demo:
demo Lesson8LockIntPerform:在使用ReentrantLock加非公平鎖的情況下100個(gè)線程循環(huán)下單數(shù)為:857239882
demo Lesson8LockIntPerform:在使用ReentrantLock加非公平鎖的情況下100個(gè)線程循環(huán)下單數(shù)為:860364303
demo Lesson8LockFairIntPerform:在使用ReentrantLock加公平鎖的情況下100個(gè)線程循環(huán)下單數(shù)為:19153640
demo Lesson8LockFairIntPerform:在使用ReentrantLock加公平鎖的情況下100個(gè)線程循環(huán)下單數(shù)為:19076567
上面的demo中,在使用公平鎖的情況下性能明顯降低,非公平鎖的性能是公平鎖性能的幾十倍以上,這和公平鎖每次試圖占有鎖時(shí),都必須先要進(jìn)等待隊(duì)列,按照FIFO的順序去獲取鎖,因此在我們的實(shí)驗(yàn)情景下,使用公平鎖的線程進(jìn)行了頻繁切換,而頻繁切換線程,性能必然會(huì)下降的厲害,這也告誡了我們?cè)趯?shí)際的開(kāi)發(fā)過(guò)程中,在需要使用公平鎖的情景下,務(wù)必要考慮線程的切換頻率。
接下來(lái)我們來(lái)看一下讀寫(xiě)鎖,通過(guò)看讀寫(xiě)鎖的實(shí)現(xiàn)源碼,我們可以發(fā)現(xiàn),讀鎖和寫(xiě)鎖共用同一個(gè)等待隊(duì)列,那么在采用非公平鎖的情況下,如果讀鎖的線程執(zhí)行時(shí)間比較長(zhǎng),并且讀鎖的并發(fā)比較高,那么寫(xiě)鎖的線程便永遠(yuǎn)都拿不到鎖,那么實(shí)際的情況會(huì)不會(huì)是這樣呢?
demo?Lesson3WriteReadLock:此demo的讀線程在不斷的占用讀鎖,按照推論,寫(xiě)鎖的線程是沒(méi)有機(jī)會(huì)獲取到鎖的,但是實(shí)際情況是寫(xiě)鎖的線程可以正常的獲取到鎖,那么是什么原因使得寫(xiě)鎖的線程可以獲取到鎖的了?通過(guò)查看源代碼,會(huì)發(fā)現(xiàn)有這樣的一個(gè)方法:
上面的方法,實(shí)現(xiàn)了一個(gè)新的讀線程獲取鎖的中斷,它會(huì)讀取等待隊(duì)列中下一個(gè)等待鎖的線程,如果它是獲取寫(xiě)鎖的線程,那么此方法返回為真,調(diào)用它的程序會(huì)把這個(gè)試圖獲取讀鎖的線程加入到等待隊(duì)列,從而終止了讀線程一直都在占有鎖的情況。
為什么加鎖?
面試中有很多時(shí)候會(huì)問(wèn)到,為什么加鎖?加鎖是起到什么作用?
而實(shí)際上在我們的開(kāi)發(fā)過(guò)程中會(huì)出現(xiàn)并發(fā)的情況,比如說(shuō)兩個(gè)人幾乎同時(shí)點(diǎn)擊了某一個(gè)按鈕,這個(gè)時(shí)候就可以簡(jiǎn)單的理解成并發(fā),那么到底誰(shuí)先誰(shuí)后? 程序中就很可能出現(xiàn)錯(cuò)誤,當(dāng)資源出現(xiàn)共享的時(shí)候,就會(huì)開(kāi)始涉及到并發(fā)了,這個(gè)時(shí)候我們就可能會(huì)用到鎖了,來(lái)鎖住某一個(gè)資源,等我用過(guò)之后,你才能動(dòng)。 這就是為什么使用鎖。
鎖的分類(lèi)
公平鎖/非公平鎖
可重入鎖
獨(dú)享鎖/共享鎖
互斥鎖/讀寫(xiě)鎖
樂(lè)觀鎖/悲觀鎖
分段鎖
偏向鎖/輕量級(jí)鎖/重量級(jí)鎖
自旋鎖
第一次分享,我們就先說(shuō)這個(gè)公平鎖和非公平鎖。之后會(huì)在后序的文章中繼續(xù)解析!
何為公平?何為非公平?在我們?nèi)粘I钪械睦斫獠痪褪菍?duì)等的就是公平,不對(duì)等的就是不公平? 其實(shí)差不多的。
公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來(lái)獲取鎖
非公平鎖是指多個(gè)線程獲取鎖的順序并不是按照申請(qǐng)鎖的順序,有可能后申請(qǐng)的線程比先申請(qǐng)的線程優(yōu)先獲取鎖。有可能,會(huì)造成優(yōu)先級(jí)反轉(zhuǎn)或者饑餓現(xiàn)象。
在JAVA的代碼中什么是公平鎖什么又是非公平的鎖呢?
一種是使用Java自帶的關(guān)鍵字synchronized對(duì)相應(yīng)的類(lèi)或者方法以及代碼塊進(jìn)行加鎖,
而另一種是ReentrantLock,前者只能是非公平鎖,而后者是默認(rèn)非公平但可實(shí)現(xiàn)公平的一把鎖。
上面的類(lèi)圖看起來(lái)很不舒服,因?yàn)殛P(guān)于ReentrantLock這個(gè)鎖,確實(shí)是沒(méi)有很好的配圖,我們可以自己畫(huà)出來(lái)理解一下
我們先看非公平鎖,我畫(huà)圖大家理解一下,就像公共廁所,不要嫌棄惡心,但是絕對(duì)容易理解
上面這幅圖,加入說(shuō)圖中的管理員是Lock,然后現(xiàn)在A來(lái)了,說(shuō)我要去廁所,這時(shí)候管理員一看,廁所沒(méi)人,那好你進(jìn)去把,然后A就進(jìn)去了。
這個(gè)時(shí)候WC里面是有A的,正在進(jìn)行式,這時(shí)候B來(lái)了,B也想去廁所
但是這個(gè)時(shí)候WC里面是有A的,然后管理員Lock看了一下,里面有人,排隊(duì)把。。。
然后這B就得憋著,去進(jìn)入隊(duì)列去排隊(duì),然后又來(lái)了個(gè)C,這時(shí)候A還在里面,C只能也去排隊(duì),
就是這個(gè)樣子的
這時(shí)候又過(guò)了一小會(huì)來(lái)了個(gè)D,也想去WC,這時(shí)候A恰好結(jié)束了,
這時(shí)候非公平鎖就上場(chǎng)了,Lock管理員一看,里面沒(méi)人,D你進(jìn)去把,這時(shí)候就是這樣的。
當(dāng)前文章:公平鎖java代碼 java實(shí)現(xiàn)公平鎖
URL鏈接:http://muchs.cn/article40/docsjho.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)、網(wǎng)站收錄、企業(yè)網(wǎng)站制作、虛擬主機(jī)、建站公司、商城網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)