java 锁_Java之线程并发的各种锁、锁、锁
因?yàn)閮芍軟]更新了...
也不是懶,這兩周確實(shí)有些忙,趕項(xiàng)目進(jìn)度趕的不亦樂乎...
終于趕在工期前,可以進(jìn)入內(nèi)測了,我也有了些時間,可以更新啦...
????線程并發(fā)鎖是很常見的問題,而且在Java中鎖的類型、概念、使用場景等等也是面試必問的,所以今天就來先簡單的說一說線程并發(fā)中常用的一下鎖。
0 1
? 公平鎖
????何為公平鎖?就字面來理解,它就是一種有公平機(jī)制,并且不會因?yàn)槟阌腥魏蔚摹氨尘啊薄ⅰ瓣P(guān)系”就可以為所欲為的鎖。在并發(fā)環(huán)境下,每個線程在獲取鎖時先會看該鎖維護(hù)的等待隊(duì)列,如果為空,或者當(dāng)前線程時等待隊(duì)列的第一個,就占有鎖,否則就會加入到等待隊(duì)列中,此后所有的線程都是以FIFO(先進(jìn)先出)的規(guī)則來執(zhí)行。
????在Java中提供了公平鎖的實(shí)現(xiàn)類,先看個代碼demo:
打印值:
可以看到線程獲取到鎖一定和啟動保持一致的,另外ReentrantLock有參構(gòu)造默認(rèn)是true,也就是開啟公平鎖,
還有一點(diǎn),代碼中有虛線的問題,是因?yàn)槲覄?chuàng)建線程是顯式的,alibaba檢測編碼規(guī)約是提示,必須使用線程池的方式創(chuàng)建線程,避免出現(xiàn)OOM。
優(yōu)點(diǎn):
????那公平鎖的有點(diǎn)也就一目了然了:所有的線程在CPU執(zhí)行調(diào)度的時候,都能夠得到資源,不至于有些線程因?yàn)閾屨疾坏劫Y源,而一直無法被執(zhí)行。
缺點(diǎn):
????有優(yōu)點(diǎn)就必然有缺點(diǎn):既然可以有序的執(zhí)行,那肯定就會降低吞吐量,隊(duì)列里面除了第一個線程,其它線程將會全部阻塞,CPU喚醒阻塞線程的開銷會很大。
0 2
非公平鎖
????何為非公平鎖?同樣字面意思理解,就是一種不公平機(jī)制的鎖,隨機(jī)搶占資源,不跟你整那一套先到先得的規(guī)則。線程加鎖時先嘗試獲取鎖,獲取不到就自動到隊(duì)尾等待。
上述同樣的代碼,把ReentrantLock的有參構(gòu)造改成false,執(zhí)行看下打印值:
非公平鎖時隨機(jī)搶占的機(jī)制,所以也會出現(xiàn)公平鎖的情況,所以測試時候不用糾結(jié)這個。
優(yōu)點(diǎn):
????非公平的性能肯定是高于公平鎖性能的,非公平鎖能更充分的利用CPU的時間片,盡量的減少CPU空閑的狀態(tài)時間。
缺點(diǎn):
????既然是隨機(jī)搶占資源的,那就肯定會有隊(duì)列中間的線程一直獲取不到鎖或長時間獲取不到鎖,導(dǎo)致“餓死”。
0 3
可重入鎖
? ?????? ?何為可重入鎖:可重復(fù)可遞歸調(diào)用的鎖,在外層使用鎖之后,在內(nèi)層仍然可以使用,并且不發(fā)生死鎖,但前提是同一個對象或同一個類中,這樣的鎖就叫可重入鎖。
????上面兩種鎖介紹的都是ReentrantLock實(shí)現(xiàn)的,而在真正的使用中保證線程安全synchronized也是不可缺少的,這兩者都是可重入鎖。
父方法和子方法同時輸出了線程的名稱,表明即使遞歸使用synchronized也沒有發(fā)生死鎖,證明其是可重入的。
那這兩者有什么區(qū)別呢?
0 4
ReentrantLock和synchronized區(qū)別
1,synchronized依賴于JVM,ReentrantLock依賴于API,需要lock()和unlock()方法配合try/finally語句塊來實(shí)現(xiàn)。
2,synchronized使用比較簡單方便,并且有編譯器去保證鎖的加鎖和釋放,而ReentrantLock需要手動聲明加鎖和釋放鎖,因此ReentrantLock鎖的粒度和靈活度要高于synchronized。
3,synchronized在優(yōu)化以前,性能方面和ReentrantLock是差很多的,但是在JDK1.6之后synchronized引入了偏向鎖和自旋鎖,兩者的性能就差不多了,官方甚至都建議使用synchronized。其實(shí)synchronized的優(yōu)化感覺就是采用ReentrantLock的CAS技術(shù)(這里不要有歧義:ReentrantLock底層是基于AQS實(shí)現(xiàn)的,但AQS的本質(zhì)其實(shí)就是volatile+CAS)。都是試圖在用戶態(tài)就把加鎖的問題解決,避免進(jìn)入內(nèi)核態(tài)的線程阻塞。
0 5
讀寫鎖
????讀寫鎖實(shí)際上就一種特殊的自旋鎖,讀寫鎖的規(guī)則可以共享讀,但只能一個寫,讀讀不互斥、讀寫互斥、寫寫互斥,而一般的獨(dú)占鎖是啥都互斥,而我們實(shí)際場景中,讀一定是大于寫的,一般情況下獨(dú)占鎖的效率低來源于高并發(fā)下對臨界區(qū)的資源搶占導(dǎo)致線程上下文切換。因此當(dāng)并發(fā)不是很高的情況下,讀寫鎖需要額外的維護(hù)讀鎖的狀態(tài),可能還不如獨(dú)占鎖的效率高,所以要根據(jù)實(shí)際場景而定。(本篇不深入將方法的實(shí)現(xiàn)和源碼)
0 6
synchronized JDK1.6優(yōu)化之后的鎖
6.1??偏向鎖
????????當(dāng)線程執(zhí)行到臨界區(qū)時,會利用CAS操作,將線程ID插入到Mark Word(對象頭中的標(biāo)記子),同時修改偏向鎖的標(biāo)志位。
????所謂臨界區(qū),就是只允許一個線程進(jìn)去執(zhí)行操作的區(qū)域,即同步代碼塊,cas是一個原子性的操作。
????這個鎖會偏向于第一個獲得它的線程,在接下來的執(zhí)行過程中,假如該鎖沒有被其它線程所獲取,沒有其它線程來競爭該鎖,那么持有偏向鎖的線程將永遠(yuǎn)不需要進(jìn)行同步操作。也就是說,在此線程之后的執(zhí)行過程中,如果再次進(jìn)入或者退出同一段同步塊代碼,并不再需要去進(jìn)行加鎖或者解鎖操作。
6.2?鎖膨脹
????綜合偏向鎖,當(dāng)出現(xiàn)有兩個線程來競爭的話,那么偏向鎖就失效了,此時鎖就會膨脹,升級為輕量鎖,這就是我們經(jīng)常說的鎖膨脹。
6.3?鎖撤銷
????當(dāng)偏向鎖失效,就得把該鎖撤銷,鎖撤銷的開銷花費(fèi)是很大的,大概的過程:
在一個安全點(diǎn)停止擁有鎖的線程。
遍歷線程棧,如果存在鎖記錄的話,需要修復(fù)鎖記錄和Markword,使其變成無鎖狀態(tài)。
喚醒當(dāng)前線程,將當(dāng)前鎖升級成輕量級鎖。所以,如果某些同步代碼塊大多數(shù)情況下都是有兩個及以上的線程競爭的話,那么偏向鎖就會是一種累贅,對于這種情況,我們可以一開始就把偏向鎖這個默認(rèn)功能給關(guān)閉
6.4?自旋鎖(jdk1.4.2引入的)
????自旋鎖屬于輕量級鎖。所謂自旋,就是指當(dāng)有另外一個線程來競爭鎖時,這個線程會在原地循環(huán)等待,而不是把該線程阻塞,直到那個獲得鎖線程釋放鎖之后,這個線程就可以馬上獲取到鎖。
????注意:鎖在原地循環(huán)的時候,是會消耗CPU的,就相當(dāng)于在循環(huán)一個啥也沒有的for循環(huán)。所以,輕量級鎖適用于那些同步代碼塊執(zhí)行的很快的場景,這樣,線程原地等待很短很短的時間就能夠獲得鎖了。
經(jīng)驗(yàn)表明,大部分同步代碼塊執(zhí)行的時間都是很短很短的,也正是基于這個原因,才有了輕量級鎖這么個東西。
自旋鎖存在的問題:
1,如果同步代碼塊執(zhí)行的很慢,需要消耗大量的時間,這個時候,其它線程在原地等待空消耗的CPU,這點(diǎn)就會有問題。
2,本來一個線程把鎖釋放之后,當(dāng)前線程是能夠獲得鎖的,但是假如這個時候好幾個線程都在競爭這個鎖的話,那么可能當(dāng)前線程會獲取不到鎖,還在原地”轉(zhuǎn)圈圈“,轉(zhuǎn)到懷疑人生。
那基于這個問題,我們就需要給線程空循環(huán)設(shè)置一個值,當(dāng)線程超過了這個次數(shù),我們就認(rèn)為繼續(xù)使用自旋鎖就不合適了,一直原地空循環(huán),這誰受得了。此時鎖就會再次膨脹,升級為重量級鎖。
默認(rèn)情況下,自旋的次數(shù)為10次,當(dāng)然可以通過-XX:PreBlockSpin來進(jìn)行更改。
6.5?自適應(yīng)自旋鎖
????所謂自適應(yīng)自旋鎖就是線程空循環(huán)等待的自旋次數(shù)并非是固定的,而是會動態(tài)著根據(jù)實(shí)際情況來改變自旋等待的次數(shù)。
其大概原理是這樣的:
????假如一個線程1剛剛成功獲得一個鎖,當(dāng)它把鎖釋放了之后,線程2獲得該鎖,并且線程2在運(yùn)行的過程中,此時線程1又想來獲得該鎖了,但線程2還沒有釋放該鎖,所以線程1只能自旋等待,但是虛擬機(jī)認(rèn)為,由于線程1剛剛獲得過該鎖,那么虛擬機(jī)覺得線程1這次自旋也是很有可能能夠再次成功獲得該鎖的,所以會延長線程1自旋的次數(shù)。
另外,如果對于某一個鎖,一個線程自旋之后,很少成功獲得該鎖,那么以后這個線程要獲取該鎖時,是有可能直接忽略掉自旋過程,直接升級為重量級鎖的,以免空循環(huán)等待浪費(fèi)資源。
輕量級鎖也被稱為非阻塞同步、樂觀鎖,因?yàn)檫@個過程并沒有把線程阻塞掛起,而是讓線程空循環(huán)等待,串行執(zhí)行。
本篇著重講的是一下名詞的概念,之后會對于ReentrantLock的源碼解析來真正的理解這些鎖的底層實(shí)現(xiàn)原理,因?yàn)橹淙幌戎渌匀焕病?/p>
- End -
總結(jié)
以上是生活随笔為你收集整理的java 锁_Java之线程并发的各种锁、锁、锁的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 巷字电脑五笔怎么打(巷五笔怎么打出来)
- 下一篇: 安卓二战风云怎么实名认证(安卓二战风云)