深入分析 Java Lock 同步鎖
- 2020 年 12 月 8 日
- 筆記
前言
Java 的鎖實現,有 Synchronized 和 Lock。上一篇文章深入分析了 Synchronized 的實現原理:由Java 15廢棄偏向鎖,談談Java Synchronized 的鎖機制。
本篇文章深入分析 Lock 的實現,以及對比其與 Synchronized 的不同。
Synchronized 與 Lock 的對比
- 實現方式:Synchronized 由 JVM 實現;Lock 由 Java 底層程式碼實現
- 鎖獲取:Synchronized 是 JVM 隱式獲取,不用 Java 程式碼;Lock 由 Java 程式碼實現,有多種獲取方式
- 鎖的釋放:Synchronized 是 JVM 隱式釋放,不用 Java 程式碼;Lock 可通過
Lock.unlock()
,在 finally 中釋放 - 鎖的類型:
Synchronized 是非公平、可重入的
,Lock 是非公平性、公平性、可重入的
- 鎖的中斷:Synchronized 不支援中斷,Lock 支援中斷
實現原理
Lock 是一個介面類,其介面方法定義:
lock()
:獲取鎖lockInterruptibly()
:如果當前執行緒未被中斷,則獲取鎖,可以響應中斷tryLock()
:僅在調用時鎖為空閑狀態才獲取該鎖,可以響應中斷tryLock(long time, TimeUnit unit)
:如果鎖在給定的等待時間內空閑,並且當前執行緒未被中斷,則獲取鎖unlock()
:釋放鎖newCondition()
:返回綁定到此 Lock 實例的新 Condition 實例
基礎原理就不贅述了,Lock 介面的常用類:
- ReentrantLock(重入鎖)
- ReentrantReadWriteLock(可重入的讀寫鎖)
其都是依賴 AQS
實現的。AQS 類結構中包含一個基於鏈表實現的等待隊列
(CLH 隊列),用於存儲所有阻塞的執行緒,AQS 中還有一個 state 變數,表示加鎖狀態。
ReentrantLock
ReentrantReadWriteLock
ReentrantLock 是獨佔鎖,對於同一份數據,如果一個執行緒讀數據,另一個執行緒在寫數據,那麼讀到的數據與最終的數據可能不一致。
在實際的業務場景中,讀操作遠遠大於寫操作。在多執行緒編程中,讀操作不會修改共享資源的數據。針對讀多寫少的場景,我們可以使用 ReentrantReadWriteLock 來優化,ReentrantReadWriteLock 內部維護了 2 個鎖:讀鎖 ReadLock
,寫鎖 WriteLock
。
規則簡單概括為:
- 如果寫鎖沒有被佔用,就可以獲取讀鎖
- 如果讀鎖沒有被佔用,才可以獲取寫鎖
下面的測試程式碼,因為讀鎖和寫鎖是同時 lock 的,所以會死鎖。
@Test
public void testReadWriteLock() {
ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
lock.readLock().lock();
lock.writeLock().lock();
System.out.println("Hello");
lock.readLock().unlock();
lock.writeLock().unlock();
}
StampedLock
上述 ReentrantReadWriteLock 會有一個問題:在讀很多,寫很少的情況下,執行緒會因一直無法獲取到鎖而處於等待狀態。
在 JDK 1.8 中,Java 提供了 StampedLock
,有三種模式:
- 寫
- 悲觀讀
- 樂觀讀
一個寫執行緒獲取寫鎖,通過 WriteLock 獲取票據 stamp,WriteLock 是一個獨佔鎖,unlockWrite 需要傳遞參數 stamp。
不一樣的地方在於讀過程。執行緒會先通過樂觀鎖 tryOptimisticRead
獲取票據 stamp,如果當前沒有執行緒持有寫鎖,則會返回一非 0 的 stamp 資訊。執行緒獲取該 stamp 後,會拷貝一份共享資源到房發展。
之後方法還需要調用 validate,驗證之前調用 tryOptimisticRead 返回的 stamp 在當前是否有其它執行緒持有了寫鎖,如果是,那麼 validate 會返回 0,升級為悲觀鎖;否則就可以使用該 stamp 版本的鎖對數據進行操作。
總結
相比於 Synchronized 同步鎖,Lock 實現的鎖更加靈活:
- 可以分為讀寫鎖,優化讀大於寫的場景
- 可以中斷
- 可以超時
- 可以區分公平性
公眾號
coding 筆記、點滴記錄,以後的文章也會同步到公眾號(Coding Insight)中,希望大家關注_
程式碼和思維導圖在 GitHub 項目中,歡迎大家 star!