我們一起聊聊 Java 中的鎖
一、偏向鎖,輕量級(jí)鎖,重量級(jí)鎖
這三種鎖特指synchronized鎖的狀態(tài),通過(guò)java對(duì)象的頭markworld來(lái)標(biāo)識(shí)鎖狀態(tài)。
偏向鎖有時(shí)候我們加鎖了,但是實(shí)際上卻不存在競(jìng)爭(zhēng),所以沒(méi)必要上鎖,只要打個(gè)標(biāo)識(shí)即可,這就是偏向鎖的思想。如果一個(gè)對(duì)象初始化后,還沒(méi)有任何線程來(lái)訪問(wèn)它,它就是可偏向的,第一個(gè)線程來(lái)訪問(wèn)它的時(shí)候,就把這個(gè)線程記錄下來(lái),如果下次仍然是這個(gè)線程訪問(wèn),因?yàn)樗沁@個(gè)偏向鎖的擁有者,所以直接獲取鎖,開(kāi)銷(xiāo)很小。
輕量級(jí)鎖如果synchronized是被java的多個(gè)線程交替訪問(wèn),不是同時(shí)競(jìng)爭(zhēng)的,或者競(jìng)爭(zhēng)的時(shí)間短,用CAS(CompareAndSet)方式來(lái)輪詢獲取鎖,不用經(jīng)過(guò)上下文切換,這種時(shí)候沒(méi)必要用重量級(jí)鎖。
輕量級(jí)鎖指原來(lái)是偏向鎖,這時(shí)候一個(gè)非偏向鎖擁有者的線程來(lái)訪問(wèn)對(duì)象,那么這個(gè)偏向鎖就升級(jí)為輕量級(jí)鎖,即通過(guò)輪詢方式來(lái)獲取鎖,不用阻塞。
重量級(jí)鎖對(duì)于競(jìng)爭(zhēng)比較激烈的場(chǎng)景,采用輕量級(jí)鎖就需要等待很長(zhǎng)時(shí)間的空轉(zhuǎn),這時(shí)候適合用重量級(jí)鎖。它利用重量級(jí)鎖的同步機(jī)制實(shí)現(xiàn),開(kāi)銷(xiāo)比較大。
鎖升級(jí)
總結(jié):偏向鎖性能最好不用CAS操作,輕量級(jí)鎖利用CAS和自旋避免重量級(jí)操作,性能次之;重量級(jí)鎖利用系統(tǒng)實(shí)現(xiàn),需要上下文切換,最終,性能最差。
二、可重入鎖/非可重入鎖
可重入鎖即獲取鎖的線程,不釋放鎖的情況下,可以再次獲取這個(gè)鎖。非可重入鎖即線程獲取鎖之后,只能釋放了鎖之后,才能再次獲取鎖。
ReentrantLock即可重入鎖。
三、共享鎖、獨(dú)占鎖
共享鎖是可以同時(shí)被多個(gè)線程同時(shí)占有的鎖,獨(dú)占鎖即只能被一個(gè)線程所占有。我們常用的讀寫(xiě)鎖,讀鎖屬于共享鎖,可以同時(shí)被多個(gè)線程占有,寫(xiě)鎖屬于獨(dú)占鎖,只能被一個(gè)線程所占用。
四、公平鎖、非公平鎖
如果鎖已經(jīng)被占用,后續(xù)要獲取鎖的線程就會(huì)等待,開(kāi)始排隊(duì),如果鎖被釋放后,等待時(shí)間最長(zhǎng)的線程獲取鎖,這就是公平鎖,非公平鎖會(huì)在一定情況下準(zhǔn)許插隊(duì)的情況。
五、悲觀鎖和樂(lè)觀鎖
悲觀鎖即在獲取資源之前,先獲取鎖,然后就進(jìn)行操作,這樣保證其他想操作的線程因?yàn)闆](méi)有獲取鎖,所以無(wú)法操作。樂(lè)觀鎖,傾向于認(rèn)為競(jìng)爭(zhēng)不激烈,它不要求操作資源前先獲取鎖,而是直接用CAS操作,即更新的時(shí)候判斷值是不是原來(lái)獲取的值,如果是就直接修改(判斷和更改是原子操作),如果不是就重試,在不獨(dú)占的時(shí)候就完成了資源的修改。
六、自旋鎖和非自旋鎖
自旋鎖的理念是如果線程拿不到鎖,不會(huì)阻塞或釋放CPU資源而是采用循環(huán)等待的方式,不斷獲取鎖,這種方式即為自旋,我理解即為不停止循環(huán)判斷鎖是否釋放了;非自旋鎖,如果沒(méi)有獲取鎖,則會(huì)進(jìn)入阻塞或做其他事情。
七、可中斷鎖和不可中斷鎖
java中synchronized關(guān)鍵字修飾的鎖為不可中斷鎖,一旦線程申請(qǐng)了鎖,需要等獲取鎖之后執(zhí)行完畢邏輯后,不可以被中斷。ReentrantLock即可中斷鎖,在獲取鎖的過(guò)程中,可以被中斷,不用必須等待其他線程釋放鎖后,再獲取鎖。
來(lái)源:今日頭條