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