Java中的輕量級(jí)鎖本質(zhì)上是一種優(yōu)化策略,用于減少無(wú)競(jìng)爭(zhēng)時(shí)重量級(jí)鎖的開銷,但并非替代品,而是在特定場(chǎng)景下嘗試獲取鎖,若失敗則升級(jí)為重量級(jí)鎖。1. 輕量級(jí)鎖通過(guò)對(duì)象頭的mark word存儲(chǔ)線程信息,嘗試用cas操作獲取鎖;2. 若cas失敗說(shuō)明存在競(jìng)爭(zhēng),則進(jìn)入自旋鎖機(jī)制,避免立即阻塞線程;3. 自旋鎖讓線程忙等待一段時(shí)間,期望快速獲得鎖,減少阻塞和喚醒的開銷;4. 若自旋期間仍無(wú)法獲取鎖,則膨脹為重量級(jí)鎖;5. 輕量級(jí)鎖并不一定比重量級(jí)鎖快,其適用于競(jìng)爭(zhēng)不激烈且鎖持有時(shí)間短的場(chǎng)景;6. 自旋次數(shù)由jdk自適應(yīng)調(diào)整,以平衡cpu資源消耗與等待效率;7. 自旋鎖實(shí)現(xiàn)方式包括簡(jiǎn)單的原子變量循環(huán)、clh鎖和mcs鎖等;8. 選擇鎖策略應(yīng)分析競(jìng)爭(zhēng)程度、持有時(shí)間、cpu資源等因素,優(yōu)先考慮無(wú)鎖方案,再根據(jù)場(chǎng)景選擇輕量級(jí)或重量級(jí)鎖,并合理調(diào)整參數(shù)。
Java中的輕量級(jí)鎖,本質(zhì)上是一種優(yōu)化策略,旨在減少在沒(méi)有實(shí)際競(jìng)爭(zhēng)情況下,重量級(jí)鎖帶來(lái)的開銷。它不是替代品,而是一種在特定場(chǎng)景下的“嘗試”。如果嘗試失敗,還是會(huì)升級(jí)到重量級(jí)鎖。自旋鎖則是輕量級(jí)鎖實(shí)現(xiàn)的一個(gè)關(guān)鍵部分,讓線程在獲取鎖失敗時(shí),不要立即阻塞,而是忙等待(自旋)一段時(shí)間,期望鎖能被快速釋放。
解決方案:
輕量級(jí)鎖的實(shí)現(xiàn),簡(jiǎn)單來(lái)說(shuō),就是利用對(duì)象頭的Mark Word來(lái)存儲(chǔ)線程信息。當(dāng)一個(gè)線程嘗試獲取鎖時(shí),會(huì)檢查Mark Word是否指向當(dāng)前線程。如果是,則表示鎖已被當(dāng)前線程持有,直接進(jìn)入同步塊。如果不是,則嘗試使用CAS(Compare and Swap)操作將Mark Word更新為指向當(dāng)前線程的指針。
立即學(xué)習(xí)“Java免費(fèi)學(xué)習(xí)筆記(深入)”;
如果CAS操作成功,則獲取鎖成功。如果CAS操作失敗,則說(shuō)明存在競(jìng)爭(zhēng),此時(shí)會(huì)膨脹為重量級(jí)鎖。而自旋鎖就在CAS失敗后發(fā)揮作用:線程不會(huì)立即進(jìn)入阻塞狀態(tài),而是循環(huán)嘗試CAS操作,這就是“自旋”。自旋一段時(shí)間后,如果仍然無(wú)法獲取鎖,才會(huì)升級(jí)為重量級(jí)鎖。
為什么需要自旋鎖?因?yàn)榫€程的阻塞和喚醒需要操作系統(tǒng)介入,開銷較大。如果鎖的持有時(shí)間很短,那么自旋等待可能比阻塞和喚醒更快。
輕量級(jí)鎖一定比重量級(jí)鎖快嗎?
不一定。輕量級(jí)鎖的優(yōu)勢(shì)在于減少了線程阻塞和喚醒的開銷,但自旋也會(huì)消耗CPU資源。如果鎖的競(jìng)爭(zhēng)非常激烈,或者鎖的持有時(shí)間很長(zhǎng),那么自旋會(huì)浪費(fèi)大量的CPU時(shí)間,反而不如直接阻塞。所以,輕量級(jí)鎖更適合于鎖競(jìng)爭(zhēng)不激烈,且鎖的持有時(shí)間較短的場(chǎng)景。
另外,自旋的次數(shù)也是一個(gè)需要考慮的因素。如果自旋次數(shù)過(guò)多,會(huì)浪費(fèi)CPU資源;如果自旋次數(shù)過(guò)少,又可能無(wú)法等到鎖的釋放。JDK中,自旋的次數(shù)是自適應(yīng)的,會(huì)根據(jù)之前的自旋情況進(jìn)行調(diào)整。
自旋鎖有哪些實(shí)現(xiàn)方式?
自旋鎖的實(shí)現(xiàn)方式有很多種,最簡(jiǎn)單的就是使用一個(gè)循環(huán)和一個(gè)原子變量。例如:
public class SpinLock { private AtomicBoolean locked = new AtomicBoolean(false); public void lock() { while (!locked.compareAndSet(false, true)) { // 自旋等待 } } public void unlock() { locked.set(false); } }
這段代碼中,compareAndSet方法嘗試將locked從false設(shè)置為true。如果設(shè)置成功,則獲取鎖;如果設(shè)置失敗,則繼續(xù)循環(huán)等待。
除了這種簡(jiǎn)單的實(shí)現(xiàn)方式,還有一些更高級(jí)的自旋鎖,例如CLH鎖和MCS鎖。這些鎖可以避免多個(gè)線程同時(shí)競(jìng)爭(zhēng)同一個(gè)原子變量,從而提高性能。
如何選擇合適的鎖策略?
選擇合適的鎖策略需要綜合考慮多個(gè)因素,包括鎖的競(jìng)爭(zhēng)程度、鎖的持有時(shí)間、CPU資源等。一般來(lái)說(shuō),可以按照以下步驟進(jìn)行選擇:
- 分析場(chǎng)景: 了解鎖的競(jìng)爭(zhēng)程度和鎖的持有時(shí)間。
- 優(yōu)先選擇無(wú)鎖方案: 如果可能,盡量使用無(wú)鎖方案,例如原子變量、CAS操作等。
- 選擇合適的鎖: 如果必須使用鎖,則根據(jù)場(chǎng)景選擇合適的鎖。如果鎖的競(jìng)爭(zhēng)不激烈,且鎖的持有時(shí)間較短,則可以選擇輕量級(jí)鎖。如果鎖的競(jìng)爭(zhēng)激烈,或者鎖的持有時(shí)間很長(zhǎng),則應(yīng)該選擇重量級(jí)鎖。
- 調(diào)整鎖的參數(shù): 根據(jù)實(shí)際情況調(diào)整鎖的參數(shù),例如自旋次數(shù)、偏向鎖延遲等。
沒(méi)有銀彈,只有最適合的方案。