一罕袋、死鎖
關(guān)于死鎖改淑,無(wú)外乎,對(duì)線程資源的爭(zhēng)搶浴讯,線程之間相互等待對(duì)方釋放資源朵夏,從而導(dǎo)致等待的一種情況。
出現(xiàn)死鎖的條件在于:
①互斥條件:一個(gè)資源每次只能被一個(gè)進(jìn)程使用
②請(qǐng)求和保持條件:一個(gè)線程因?yàn)檎?qǐng)求資源的時(shí)候?qū)е伦枞芘Γ簿褪菬o(wú)法請(qǐng)求到其他資源的時(shí)候仰猖,自身阻塞,但是又無(wú)法釋放自己所擁有的資源奈籽,從而使其他想要獲取其資源的線程繼續(xù)等待饥侵。
③不剝奪條件:對(duì)于一個(gè)擁有某個(gè)資源的線程,如果這個(gè)線程沒(méi)有完成其自身的工作衣屏,那么躏升,其他線程無(wú)法強(qiáng)行剝奪。
④循環(huán)等待關(guān)系:一系列線程之間形成相互等待的一種循環(huán)等待資源的關(guān)系狼忱。
二膨疏、線程狀態(tài)
首先得知道線程的狀態(tài):
①新建狀態(tài)(New) 新創(chuàng)建了一個(gè)線程對(duì)象。
②就緒狀態(tài)(Runnable) 線程對(duì)象創(chuàng)建后钻弄,其他線程調(diào)用了該對(duì)象的start()方法佃却。該狀態(tài)的線程位于可運(yùn)行線程池中,變得可運(yùn)行窘俺,等待獲取CPU的使用權(quán)饲帅。
③運(yùn)行狀態(tài)(Running) 就緒狀態(tài)的線程獲取了CPU,執(zhí)行程序代碼批销。
④阻塞狀態(tài)(Blocked) 阻塞狀態(tài)是線程因?yàn)槟撤N原因放棄CPU使用權(quán)洒闸,暫時(shí)停止運(yùn)行。直到線程進(jìn)入就緒狀態(tài)均芽,才有機(jī)會(huì)轉(zhuǎn)到運(yùn)行狀態(tài)丘逸。阻塞的情況分三種:
-------等待阻塞:運(yùn)行的線程執(zhí)行wait()方法,JVM會(huì)把該線程放入等待池中掀宋。
-------同步阻塞:運(yùn)行的線程在獲取對(duì)象的同步鎖時(shí)深纲,若該同步鎖被別的線程占用,則JVM會(huì)把該線程放入鎖池中劲妙。
-------其他阻塞:運(yùn)行的線程執(zhí)行sleep()或join()方法湃鹊,或者發(fā)出了I/O請(qǐng)求時(shí),JVM會(huì)把該線程置為阻塞狀態(tài)镣奋。當(dāng)sleep()狀態(tài)超時(shí)币呵、join()等待線程終止或者超時(shí)、或者I/O處理完畢時(shí)侨颈,線程重新轉(zhuǎn)入就緒狀態(tài)余赢。
⑤死亡狀態(tài)(Dead):線程執(zhí)行完了或者因異常退出了run()方法芯义,該線程結(jié)束生命周期
其狀態(tài)變化過(guò)程:
當(dāng)執(zhí)行new Thread(Runnabler)后,新創(chuàng)建出來(lái)的線程處于new狀態(tài)妻柒,這種線程不可能執(zhí)行扛拨。
當(dāng)執(zhí)行thread.start()后,線程處于runnable狀態(tài)举塔,這種情況下只要得CPU绑警,就可以開(kāi)始執(zhí)行了。runnable狀態(tài)的線程央渣,會(huì)接受JVM的調(diào)度计盒,進(jìn)入running狀態(tài),但是具體何時(shí)會(huì)進(jìn)入這個(gè)狀態(tài)痹屹,是隨機(jī)不可知的running狀態(tài)中的線程最為復(fù)雜章郁,可能會(huì)進(jìn)入runnable、waiting志衍、timed_waiting暖庄、blocked、dead狀態(tài):如果CPU調(diào)度給了別的線程楼肪,或者執(zhí)行了Thread.yield()方法培廓,則進(jìn)入runnable狀態(tài),但是也有可能立刻又進(jìn)入running狀態(tài)如果執(zhí)行了Thread.sleep(long)春叫,或者thread.join(long)肩钠,或者在鎖對(duì)象上調(diào)用object.wait(long)方法,則會(huì)進(jìn)入timed_waiting狀態(tài)如果執(zhí)行thread.join()暂殖,或者在鎖對(duì)象上調(diào)用了object.wait()方法价匠,則會(huì)進(jìn)入waiting狀態(tài)如果進(jìn)入了同步方法或者同步代碼塊,沒(méi)有獲取鎖對(duì)象的話呛每,則會(huì)進(jìn)入blocked狀態(tài)
處于waiting狀態(tài)中的線程踩窖,如果是因?yàn)閠hread.join()方法進(jìn)入等待的,在目標(biāo)thread執(zhí)行完畢之后晨横,會(huì)回到runnable狀態(tài)洋腮;如果是因?yàn)閛bject.wait()方法進(jìn)入等待的話,在鎖對(duì)象執(zhí)行object.notify()或者object.notifyAll()之后會(huì)回到runnable狀態(tài)手形,處于timed_waiting狀態(tài)中的線程啥供,和waiting狀態(tài)中的差不多,只不過(guò)是設(shè)定時(shí)間到了库糠,就會(huì)回到runnable狀態(tài)處于blocked狀態(tài)中的線程伙狐,只有獲取了鎖之后,才會(huì)脫離阻塞狀態(tài)。當(dāng)線程執(zhí)行完畢鳞骤,或者拋出了未捕獲的異常之后窒百,會(huì)進(jìn)入dead狀態(tài)黍判,該線程結(jié)束豫尽。
三、常見(jiàn)鎖的優(yōu)化
(一)代碼層面
1顷帖、減少鎖持有的時(shí)間
比如:
public synchronized void syncMethod(){
othercode1();
mutextMethod();
othercode2();
}
實(shí)際的更新操作是在mutextMethod()方法美旧,所以對(duì)整個(gè)方法加鎖并沒(méi)有必要。
所以贬墩,為了減少鎖持有的時(shí)間榴嗅,可以只將mutextMethod()加類鎖,作用域當(dāng)前實(shí)例對(duì)象陶舞;
public void syncMethod(){
othercode1();
synchronize(this){
mutextMethod();
}
othercode2();
}
2嗽测、減小鎖的顆粒度
即只鎖住相關(guān)容易造成并發(fā)錯(cuò)誤的地方,同時(shí)又要實(shí)現(xiàn)線程安全肿孵,concurrentHashMap 與 hashMap之間即實(shí)現(xiàn)了減小鎖的顆粒度的一個(gè)優(yōu)化唠粥。
**:對(duì)于concurrentHashMap有一個(gè)重要的點(diǎn),concurrentHashMap的size()方法停做,會(huì)優(yōu)先使用無(wú)鎖的方式來(lái)求和晤愧,但是如果失敗則會(huì)嘗試取得所有“子段”的鎖,所以蛉腌,在高并發(fā)場(chǎng)合官份,concurrentHashMap的size()方法性能要比hashMap的性能要差。
3烙丛、鎖分離(讀寫分離鎖)
即對(duì)功能的劃分加鎖舅巷,比如讀寫分離鎖,讀讀不互斥河咽,讀寫互斥钠右,寫寫互斥。
4库北、鎖粗化
如果對(duì)一個(gè)鎖爬舰,不停的獲取和釋放,其本身也會(huì)消耗系統(tǒng)資源寒瓦,所以對(duì)于一連串的對(duì)同一個(gè)對(duì)象進(jìn)行加鎖情屹,倒不如將這段合在一起加鎖。這即鎖的粗化杂腰。
(二)JVM層面
1垃你、自旋鎖
在多線程并發(fā)條件下,頻繁的掛起和恢復(fù)線程的操作會(huì)給系統(tǒng)帶來(lái)極大的壓力,特別是當(dāng)訪問(wèn)共享資源僅需要花費(fèi)很小一段CPU時(shí)間時(shí)候惜颇,為了這段時(shí)間去做重量級(jí)的線程切換是不值得的皆刺。
自旋鎖就是可以使線程在沒(méi)有取得鎖的時(shí)候,不被掛起凌摄,而轉(zhuǎn)而去執(zhí)行一個(gè)空循環(huán)(即所謂的自旋)羡蛾,在若干個(gè)空循環(huán)后,線程如果獲得了鎖锨亏,則繼續(xù)執(zhí)行痴怨,若線程依然不能獲得鎖,才會(huì)被掛起器予。
當(dāng)然自旋鎖不適用與競(jìng)爭(zhēng)激烈浪藻,單線程鎖占用時(shí)間長(zhǎng)的并發(fā)程序。
JAVA虛擬機(jī)提供: -XX:+UseSinning參數(shù)來(lái)開(kāi)啟自旋鎖乾翔,使用
-XX:PreBlockSpin參數(shù)來(lái)設(shè)置自旋鎖的等待次數(shù)爱葵。
2、鎖消除
鎖消除是JVM在即時(shí)編譯時(shí)反浓,通過(guò)對(duì)運(yùn)行上下文的掃描萌丈,去除不可能存在共享資源競(jìng)爭(zhēng)的鎖,通過(guò)鎖消除勾习,可以節(jié)省毫無(wú)意義的請(qǐng)求時(shí)間浓瞪。
JVM虛擬機(jī)可以在運(yùn)行時(shí)基于逃逸分析技術(shù),捕獲到這些不可能存在競(jìng)爭(zhēng)卻有申請(qǐng)鎖的代碼段巧婶,并消除這些不必要的鎖乾颁,從而提高性能。
比如下面這段代碼:
protected String craeteStringBuffer(String a , String b){
StringBuffer sb = new StringBuffer(); //局部變量艺栈,沒(méi)有逃逸出這個(gè)方法
sb.append(a); //append()中有鎖的申請(qǐng)英岭,但是在當(dāng)前環(huán)境下是沒(méi)有必要的
sb.append(b);
return sb.toString();
}
逃逸分析和鎖消除分別可以使用JVM參數(shù) -XX:+DoEscapeAnalysis 和 -XX:+EliminateLocks開(kāi)啟(鎖消除必須工作在server模式下)
使用參數(shù) -server -XX:-DoEscapeAnalysis -XX:EliminateLocks關(guān)閉逃逸分析和鎖消除。