一、概述
Lock 有三個實現類,一個是 ReentrantLock, 另兩個是 ReentrantReadWriteLock 類中的兩個靜態內部類 ReadLock 和 WriteLock。
LOCK 的實現類其實都是構建在 AbstractQueuedSynchronizer 上,爲何圖中沒有用 UML 線表示呢,這是每個 Lock 實現類都持有自己內部類 Sync 的實例,而這個 Sync 就是繼承 AbstractQueuedSynchronizer (AQS)。爲何要實現不同的 Sync 呢?這和每種 Lock 用途相關。另外還有 AQS 的 State 機制。
FairSync 與 NonfairSync 的區別在於,是不是保證獲取鎖的公平性,因爲默認是 NonfairSync(非公平性)
二、AQS
可以看到Lock鎖的底層實現是AQS
1.定義
AQS(AbstractQuenedSynchronizer ),抽象的隊列式同步器,除了 java 自帶的 synchronized 關鍵字之外的鎖機制。
2.AQS 的核心思想
如果被請求的共享資源空閒,則將當前請求資源的線程設置爲有效的工作線程,並將共享資源設置爲鎖定狀態,如果被請求的共享資源被佔用,那麼就需要一套線程阻塞等待以及被喚醒時鎖分配的機制,這個機制 AQS 是用 CLH 隊列鎖(CLH 鎖是一個自旋鎖。能確保無飢餓性。提供先來先服務的公平性)實現的,即將暫時獲取不到鎖的線程加入到隊列中。
AQS 是將每一條請求共享資源的線程封裝成一個 CLH 鎖隊列的一個結點(Node),來實現鎖的分配。
3.實現
AQS 基於 CLH 隊列,用 volatile 修飾共享變量 state,線程通過 CAS 去改變狀態符,成功則獲取鎖成功,失敗則進入等待隊列,等待被喚醒。
4.總結
- lock 的存儲結構:一個 int 類型狀態值(用於鎖的狀態變更),一個雙向鏈表(用於存儲等待中的線程)
- lock 獲取鎖的過程:本質上是通過 CAS 來獲取狀態值修改,如果當場沒獲取到,會將該線程放在線程等待鏈表中。
- lock 釋放鎖的過程:修改狀態值,調整等待鏈表。
可以看到在整個實現過程中,lock 大量使用 CAS + 自旋。因此根據 CAS 特性,lock 建議使用在低鎖衝突的情況下。目前 java1.6 以後,官方對 synchronized 做了大量的鎖優化(偏向鎖、自旋、輕量級鎖)。因此在非必要的情況下,建議使用 synchronized 做同步操作。
【Java 面試那點事】
這裏致力於分享 Java 面試路上的各種知識,無論是技術還是經驗,你需要的這裏都有!
這裏可以讓你【快速瞭解 Java 相關知識】,並且【短時間在面試方面有跨越式提升】
面試路上,你不孤單!