加鎖過程中,處理沖突的過程中,涉及到的一些不同的處理方式。鎖的策略決定了線程如何獲取和釋放鎖以及在何種情況下阻塞和喚醒線程。
1. 常見的鎖策略?
1.1? 樂觀鎖和悲觀鎖
- 樂觀鎖:在加鎖之前,預估當前出現鎖沖突的概率不大,因此在進行加鎖的時候不會做太多的工作,于是加鎖的速度就可能更快,當更容易引入一些其他的問題,例如肯能會消耗更多的CPU資源。
- 悲觀鎖:在加鎖之前,預估當前出現鎖沖突的概率比較大,因此在加鎖的時候就會做更多的工作,所以加鎖的速度可能會更慢,但是整個過程中不容易出現其他問題。
1.2?輕量級鎖和重量級鎖
- 輕量級鎖:加鎖的開銷更小,加鎖速度更快。輕量級鎖一般就是樂觀鎖。
- 重量級鎖:加鎖的開銷更大,加鎖速度更慢。重量級鎖一般也就是悲觀鎖。
注意:
- 輕量級鎖和重量級鎖的定義是加鎖后對結果的評價而來
- 樂觀鎖和悲觀鎖的定義是對未發生的事做出預估而來?
?1.3?自旋鎖和掛起等待鎖
- 自旋鎖:自旋鎖就是輕量級鎖的一種典型實現,進行加鎖的時候搭配一個while循環,如果加鎖成功則循環結束,加鎖失敗不進入阻塞而是再次嘗試。這個反復快速執行的過程就稱為“自旋”。一旦其他線程釋放了鎖能第一時間拿到,同時這樣的自旋鎖也是樂觀鎖,使用自旋的前提就是預期沖突概率不大,其他線程釋放了鎖,就能第一時間拿到。如果當前加鎖的線程特別多,使用自旋鎖則會浪費過多的CPU資源。
- 掛起等待鎖:掛起等待鎖就是悲觀鎖的一種實現,同時也是重量級鎖,加鎖失敗時進入阻塞等待,不會繼續消耗CPU資源,掛起等待的時候需要內核調度器介入,這部分要完成的操作很多,所以真正獲取到鎖的時間會更長,適用于鎖沖突激烈的情況。
1.4 普通互斥鎖和讀寫鎖?
- 普通互斥鎖:普通互斥鎖也被稱為排它鎖,它確保在任何時刻只有一個線程可以獲得鎖并訪問共享資源。當一個線程持有鎖時,其他線程需要等待鎖的釋放才能繼續執行,類似于synchronized 加鎖解鎖。
- 讀寫鎖:讀寫鎖把鎖分為兩種情況,加讀鎖和加寫鎖。讀鎖和讀鎖之間,不會出現鎖沖突,即允許多個線程同時讀取共享資源;寫鎖和寫鎖之間會出現鎖沖突;讀鎖和寫鎖之間會出現鎖沖突。
?為什么要引入讀寫鎖?
如果兩個線程讀同一個數據,這個操作本身就是線程安全的,不需要阻塞,如果使用 synchronized 加鎖,則會阻塞,對于性能有一定影響。
1.5 公平鎖和非公平鎖?
- 公平鎖:公平鎖按照請求鎖的順序分配鎖資源即 “先來后到” ,保證每個線程都有公平的機會獲得鎖。這種策略避免了線程饑餓現象,但會導致額外的開銷,因為線程可能需要等待其他線程釋放鎖。
- 非公平鎖:不遵守 “先來后到” 的規則,所有線程都有可能獲取到鎖。
1.6 可重入鎖和不可重入鎖?
- ?可重入鎖:一個線程使用一把鎖連續加鎖兩次,不會產生死鎖,就是可重入鎖,如 synchronized。
- 不可重入鎖:一個線程使用一把鎖連續加鎖兩次,會產生死鎖,則是不可重入鎖。
?
2. synchronized
2.1?synchronized 的鎖策略
synchronized 具有自適應能力
synchronized 在某些情況下是 樂觀鎖/輕量級鎖/自旋鎖 有些情況下是 悲觀鎖/重量級鎖/掛起等待鎖。
內部會自動評估當前鎖沖突的激烈程度。
- 如果當前鎖沖突的激烈程度小,就處于 樂觀鎖/輕量級鎖/自旋鎖。
- 如果當前鎖沖突的激烈程度大,就處于 悲觀鎖/重量級鎖/掛起等待鎖。
2.2 鎖升級
當線程執行到 synchronized 的時候,如果鎖還是空閑的,就會經歷以下過程:
- 偏向鎖階段:每個鎖對象中有一個偏向鎖標記,當這個鎖對象首次被加鎖時,會進入偏向鎖,鎖對象會記錄下該線程的id,如果下次加鎖,沒有鎖競爭,并且仍然是同一個線程拿到鎖,這個鎖仍然是偏向鎖。偏向鎖并不會真的加上鎖,由于兩次都是同一個線程來獲取鎖,所以認為沒有鎖競爭,所以就不用真的加上鎖,免去了一定開銷。
注意:如果一個鎖此前都是線程1的偏向鎖,在某次線程1再次嘗試加鎖時,出現了一個線程2 也嘗試加鎖,此時這個鎖會升級為輕量級鎖,然后再由這兩個線程來競爭。但是本次加鎖一定是線程1拿到鎖,線程2下次競爭才可能拿到鎖。
? - 輕量級鎖階段:當偏向鎖出現鎖競爭時,或者本次加鎖的線程與第一次記錄的不同,這個鎖則會升級為輕量級鎖。此處是通過自旋鎖的方式來實現的。
? - 重量級鎖階段:當參與鎖競爭的線程達到某個閾值,就會從輕量級鎖升級到重量級鎖。
注意:鎖的升級不可逆。
2.3 鎖消除?
鎖消除也是 synchronized 中內置的優化策略。
?編譯器編譯代碼的時候,如果發現這個代碼不需要加鎖,就會取消掉這個鎖。但是這個優化是比較有限的,如果代碼稍微復雜一些,編譯器是判斷不了是否需要加鎖的。
2.4 鎖粗化?
會把多個加鎖代碼塊合成一個代碼塊,去除了多次加鎖解鎖的開銷。
例如:
synchronized(locker) {a++;
}
synchronized(locker) {b++;
}
可粗化為:
synchronized(locker) {a++;b++;
}