前言
繼之前synchronized關(guān)鍵字之后烹玉,這里旨在介紹常見的鎖概念驰怎,源碼相關(guān)。
內(nèi)容會(huì)進(jìn)行不定時(shí)更新二打,希望能更完善的整理鎖相關(guān)知識(shí)點(diǎn)县忌。
公平鎖和非公平鎖
1.公平鎖
多個(gè)線程按照申請(qǐng)鎖的順序來獲取鎖,線程直接進(jìn)入隊(duì)列中排隊(duì)继效,隊(duì)列中的第一個(gè)線程才能獲得鎖症杏。
- 優(yōu)點(diǎn):等待鎖的線程不會(huì)餓死,按照順序獲取鎖瑞信。
- 缺點(diǎn):由于CPU喚醒線程的開銷大厉颤,所以每次都需要主動(dòng)喚醒第一個(gè)線程,相對(duì)性能較差
2.非公平鎖
多個(gè)線程加鎖時(shí)直接嘗試獲取鎖凡简,獲取不到才會(huì)到等待隊(duì)列的隊(duì)尾等待逼友。
- 優(yōu)點(diǎn):當(dāng)線程嘗試獲取鎖時(shí)绩郎,正好可用,則直接獲取鎖翁逞,減少了喚醒線程的開銷肋杖。
- 缺點(diǎn):處于等待隊(duì)列中的線程可能會(huì)餓死,或者等很久才會(huì)獲得鎖挖函。
ReentrantLock
ReentrantLock支持公平鎖和非公平鎖状植。
ReentrantLock里面有一個(gè)內(nèi)部類Sync,Sync繼承AQS(AbstractQueuedSynchronizer)怨喘,添加鎖和釋放鎖的大部分操作實(shí)際上都是在Sync中實(shí)現(xiàn)的津畸。它有公平鎖FairSync和非公平鎖NonfairSync兩個(gè)子類。ReentrantLock默認(rèn)使用非公平鎖public ReentrantLock() { sync = new NonfairSync(); }
必怜,也可以通過構(gòu)造器來顯示的指定使用公平鎖public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); }
肉拓。
公平鎖與非公平鎖的lock()方法唯一的區(qū)別就在于公平鎖在獲取同步狀態(tài)時(shí)多了一個(gè)限制條件:hasQueuedPredecessors(),主要是判斷當(dāng)前線程是否位于同步隊(duì)列中的第一個(gè)梳庆。
可重入鎖和非可重入鎖
1.可重入鎖
可重入鎖指在同一個(gè)線程在外層方法獲取鎖的時(shí)候暖途,再進(jìn)入該線程的內(nèi)層方法會(huì)自動(dòng)獲取鎖。
ReentrantLock和synchronized都是可重入鎖膏执,可重入鎖的一個(gè)優(yōu)點(diǎn)是可一定程度避免死鎖驻售。
2.不可重入鎖
不可重入鎖指的是一個(gè)線程必須等其他或者同一個(gè)線程釋放鎖才能獲取鎖,所以更米,當(dāng)當(dāng)一個(gè)方法獲取到鎖之后欺栗,內(nèi)部還有其他方法或者代碼塊嘗試獲取鎖,且為不可重入鎖征峦,此時(shí)迟几,就是發(fā)生死鎖。
ReentrantLock和NonReentrantLock
ReentrantLock和NonReentrantLock都繼承父類AQS栏笆,其父類AQS中維護(hù)了一個(gè)同步狀態(tài)status來計(jì)數(shù)重入次數(shù)类腮,status初始值為0。
當(dāng)線程嘗試獲取鎖時(shí)竖伯,可重入鎖先嘗試獲取并更新status值存哲,如果status == 0表示沒有其他線程在執(zhí)行同步代碼,則把status置為1七婴,當(dāng)前線程開始執(zhí)行祟偷。如果status != 0,則判斷當(dāng)前線程是否是獲取到這個(gè)鎖的線程打厘,如果是的話執(zhí)行status+1修肠,且當(dāng)前線程可以再次獲取鎖。而非可重入鎖是直接去獲取并嘗試更新當(dāng)前status的值户盯,如果status != 0的話會(huì)導(dǎo)致其獲取鎖失敗嵌施,當(dāng)前線程阻塞饲化。
釋放鎖時(shí),可重入鎖同樣先獲取當(dāng)前status的值吗伤,在當(dāng)前線程是持有鎖的線程的前提下吃靠。如果status-1 == 0,則表示當(dāng)前線程所有重復(fù)獲取鎖的操作都已經(jīng)執(zhí)行完畢足淆,然后該線程才會(huì)真正釋放鎖巢块。而非可重入鎖則是在確定當(dāng)前線程是持有鎖的線程之后,直接將status置為0巧号,將鎖釋放族奢。
獨(dú)享鎖和共享鎖
1.獨(dú)享鎖
獨(dú)享鎖是指該鎖一次只能被一個(gè)線程所持有。
如果線程T對(duì)數(shù)據(jù)A加上獨(dú)享鎖后丹鸿,則其他線程不能再對(duì)A加任何類型的鎖越走。獲得獨(dú)享鎖的線程即能讀數(shù)據(jù)又能修改數(shù)據(jù)。
JDK中的synchronized和JUC中Lock的實(shí)現(xiàn)類就是互斥鎖靠欢。
2.共享鎖
共享鎖是指該鎖可被多個(gè)線程所持有廊敌。
如果線程T對(duì)數(shù)據(jù)A加上共享鎖后,則其他線程只能對(duì)A再加共享鎖掺涛,不能加排它鎖庭敦。獲得共享鎖的線程只能讀數(shù)據(jù),不能修改數(shù)據(jù)薪缆。
ReentrantReadWriteLock和ReentrantLock
ReentrantReadWriteLock
中分為ReadLock
和WriteLock
,鎖主體都是Sync伞广,但讀鎖和寫鎖的加鎖方式不一樣拣帽。讀鎖是共享鎖,寫鎖是獨(dú)享鎖嚼锄。讀鎖的共享鎖可保證并發(fā)讀非常高效减拭,而讀寫、寫讀区丑、寫寫的過程互斥拧粪,因?yàn)樽x鎖和寫鎖是分離的。所以ReentrantReadWriteLock的并發(fā)性相比一般的互斥鎖有了很大提升沧侥。
由源碼可知tryAcquire
實(shí)現(xiàn)了寫鎖可霎,一個(gè)線程嘗試獲取鎖,當(dāng)無讀鎖或者是當(dāng)前線程已經(jīng)得到寫鎖(可重入鎖)時(shí)宴杀,才能獲取鎖癣朗。
由源碼可知tryAcquireShared
實(shí)現(xiàn)了讀鎖,如果其他線程獲取寫鎖旺罢,其他線程獲取鎖失敗旷余,進(jìn)入等待狀態(tài)绢记。
ReentrantLock
中實(shí)現(xiàn)了公平鎖FairSync
和非公平鎖NonfairSync
,可以看到都是獨(dú)享鎖。