公平鎖和非公平鎖
公平鎖是指多個(gè)線程按照申請鎖的順序來獲取鎖,線程直接進(jìn)入隊(duì)列中排隊(duì),隊(duì)列中的第一個(gè)線程才能獲得鎖。公平鎖的優(yōu)點(diǎn)是等待鎖的線程不會(huì)餓死。缺點(diǎn)是整體吞吐效率相對非公平鎖要低,等待隊(duì)列中除第一個(gè)線程以外的所有線程都會(huì)阻塞。
非公平鎖是線程獲取鎖時(shí),直接嘗試獲取鎖,獲取不到才會(huì)到等到隊(duì)列的隊(duì)尾等待。但如果此時(shí)鎖剛好可用,那么這個(gè)線程可用無需阻塞直接獲取鎖,所以非公平鎖有可能出現(xiàn)后申請鎖的線程先獲取鎖的場景。非公平鎖的優(yōu)點(diǎn)是可以減少喚起線程的開銷,整體的吞吐效率高,因?yàn)榫€程有幾率不阻塞直接獲得鎖,CPU不必喚醒所有線程。缺點(diǎn)是處于等待隊(duì)列中的線程可能會(huì)餓死,或者等很久才會(huì)獲得鎖。
ReentrantLock中實(shí)現(xiàn)了公平鎖和非公平鎖:在代碼中的具體體現(xiàn)如下:


區(qū)別在于多了hasQueuedPredecessor()方法的限制條件,

判斷當(dāng)前線程是否位于同步隊(duì)列中的第一個(gè)。如果是則返回true,否則返回false。
公平鎖就是通過同步隊(duì)列來實(shí)現(xiàn)多個(gè)線程按照申請鎖的順序來獲取鎖,從而實(shí)現(xiàn)公平的特性。非公平鎖加鎖時(shí)不考慮排隊(duì)等待問題,直接嘗試獲取鎖,所以存在后申請卻先獲得鎖的情況。
可重入鎖和非可重入鎖
可重入鎖是在同一個(gè)線程在外層方法獲取鎖的時(shí)候,再進(jìn)入該線程的內(nèi)層方法會(huì)自動(dòng)獲取鎖(前提鎖對象得是同一個(gè)對象或者class),不會(huì)因?yàn)橹耙呀?jīng)獲取過還沒釋放而阻塞。Java中ReentrantLock和synchronized都是可重入鎖,可重入鎖的一個(gè)優(yōu)點(diǎn)是可一定程度避免死鎖。


獲?。?/b>ReentrantLock中維護(hù)一個(gè)同步狀態(tài)status來計(jì)數(shù)重入次數(shù),status初始值為0。當(dāng)獲取鎖時(shí),如果status==0表示沒有其他線程在執(zhí)行同步代碼,就獲取鎖開始執(zhí)行,并設(shè)置當(dāng)前線程擁有鎖。如果status != 0,則判斷當(dāng)前線程是否是獲取到這個(gè)鎖的線程,如果是的話執(zhí)行status+1,且當(dāng)前線程可以再次獲取鎖。NonReentrantLock中的非可重入鎖直接嘗試獲取鎖,如果獲取失敗當(dāng)前線程阻塞。


釋放:ReentrantLock先獲取當(dāng)前status的值,在當(dāng)前線程是持有鎖的線程的前提下。如果status-1 == 0,則表示當(dāng)前線程所有重復(fù)獲取鎖的操作都已經(jīng)執(zhí)行完畢,然后該線程真正釋放鎖。NonReentrantLock在確定當(dāng)前線程是持有鎖的線程之后,直接將status置為0,將鎖釋放。
獨(dú)享鎖和共享鎖
獨(dú)享鎖也叫排他鎖(寫鎖),是指該鎖一次只能被一個(gè)線程所持有。如果線程T對數(shù)據(jù)A加上排它鎖后,則其他線程不能再對A加任何類型的鎖。(synchronized)
共享鎖(讀鎖)是指該鎖可被多個(gè)線程所持有。如果線程T對數(shù)據(jù)A加上共享鎖后,則其他線程只能對A再加共享鎖,不能加排它鎖。獲得共享鎖的線程只能讀數(shù)據(jù),不能修改數(shù)據(jù)。
ReentrantReadWriteLock中持有兩把鎖。

讀寫鎖是依靠內(nèi)部Sync變量來實(shí)現(xiàn)的,是通過state字段來描述有多少線程持有鎖。在ReentrantReadWriteLock中有讀、寫兩把鎖,所以需要在整型變量state上分別描述讀鎖和寫鎖的數(shù)量。將state變量“按位切割”切分成了兩個(gè)部分,高16位表示讀鎖狀態(tài)(讀鎖個(gè)數(shù)),低16位表示寫鎖狀態(tài)(寫鎖個(gè)數(shù))。


寫鎖的加鎖步驟:
1,獲取state變量的。表示有多少線程持有鎖。
2,獲取寫鎖的個(gè)數(shù):w
3,如果state!=0,表示已經(jīng)有線程持有了鎖。
???? 3.1,如果寫鎖為0(這種情況下就存在讀鎖)&&持有鎖的線程不是當(dāng)前線程:返回失敗。
???? 3.2,存在寫鎖,但是如果寫入鎖的數(shù)量大于65535,拋出一個(gè)error。
???? 3.3,state = state+1;
4,如果沒有線程持有鎖,并且當(dāng)前線程需要阻塞||通過CAS增加寫線程數(shù)失?。悍祷厥?。
5,以上條件都不滿足,設(shè)置當(dāng)前線程是鎖的擁有者:返回true。這樣情況state和w的情況存在以下情況:c=0,w=0(第一次加鎖的情況);c>0,w>0(寫鎖大于0,是因?yàn)檫@個(gè)鎖是可重入鎖)。

if (exclusiveCount(c) != 0 && getExclusiveOwnerThread() != current) return -1;? // 如果存在寫鎖,那么加讀鎖失敗。
從以上讀鎖和寫鎖的加鎖過程可以看出,一旦線程A成功獲取到寫鎖之后,其他線程無論是讀鎖還是寫鎖都加不上。線程A可以利用到獲取的寫鎖重復(fù)使用鎖。只有等到線程A釋放了寫鎖之后,其他線程才能加鎖。在沒有任何線程添加寫鎖的情況下,任何線程都可以添加讀鎖。
ReentrantLock中有公平鎖和非公平鎖,在獲取鎖時(shí),如果同步資源沒有被其他線程鎖住,那么當(dāng)前線程在使用CAS更新state成功后就會(huì)成功搶占該資源;如果公共資源被占用且不是被當(dāng)前線程占用,那么就會(huì)加鎖失敗。所以ReentrantLock中的無論是讀操作和寫操作,加的鎖都是排他鎖。
參考: