前言
繼之前synchronized關(guān)鍵字之后,這里旨在介紹常見的鎖概念,源碼相關(guān)。
內(nèi)容會進(jìn)行不定時(shí)更新,希望能更完善的整理鎖相關(guān)知識點(diǎn)。
公平鎖和非公平鎖
1.公平鎖
多個(gè)線程按照申請鎖的順序來獲取鎖,線程直接進(jìn)入隊(duì)列中排隊(duì),隊(duì)列中的第一個(gè)線程才能獲得鎖。
- 優(yōu)點(diǎn):等待鎖的線程不會餓死,按照順序獲取鎖。
- 缺點(diǎn):由于CPU喚醒線程的開銷大,所以每次都需要主動(dòng)喚醒第一個(gè)線程,相對性能較差
2.非公平鎖
多個(gè)線程加鎖時(shí)直接嘗試獲取鎖,獲取不到才會到等待隊(duì)列的隊(duì)尾等待。
- 優(yōu)點(diǎn):當(dāng)線程嘗試獲取鎖時(shí),正好可用,則直接獲取鎖,減少了喚醒線程的開銷。
- 缺點(diǎn):處于等待隊(duì)列中的線程可能會餓死,或者等很久才會獲得鎖。
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)層方法會自動(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的話會導(dǎo)致其獲取鎖失敗,當(dāng)前線程阻塞。
釋放鎖時(shí),可重入鎖同樣先獲取當(dāng)前status的值,在當(dāng)前線程是持有鎖的線程的前提下。如果status-1 == 0,則表示當(dāng)前線程所有重復(fù)獲取鎖的操作都已經(jīng)執(zhí)行完畢,然后該線程才會真正釋放鎖。而非可重入鎖則是在確定當(dāng)前線程是持有鎖的線程之后,直接將status置為0,將鎖釋放。
獨(dú)享鎖和共享鎖
1.獨(dú)享鎖
獨(dú)享鎖是指該鎖一次只能被一個(gè)線程所持有。
如果線程T對數(shù)據(jù)A加上獨(dú)享鎖后,則其他線程不能再對A加任何類型的鎖。獲得獨(dú)享鎖的線程即能讀數(shù)據(jù)又能修改數(shù)據(jù)。
JDK中的synchronized和JUC中Lock的實(shí)現(xiàn)類就是互斥鎖。
2.共享鎖
共享鎖是指該鎖可被多個(gè)線程所持有。
如果線程T對數(shù)據(jù)A加上共享鎖后,則其他線程只能對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ú)享鎖。