Java并發(fā)鎖機制知識總結(最新整理)
并發(fā)鎖機制知識總結
Java 的鎖機制是并發(fā)編程的核心,用于解決多線程共享資源競爭問題,保證數(shù)據(jù)一致性和線程安全。Java 提供了內置鎖(synchronized)、顯式鎖(Lock 接口) 等多種鎖實現(xiàn),同時衍生出偏向鎖、輕量級鎖、重量級鎖等優(yōu)化機制,以及可重入鎖、公平鎖、讀寫鎖等功能分類。
一、鎖的核心作用
- 互斥性:同一時間只有一個線程能持有鎖,訪問共享資源(解決 “競態(tài)條件”)。
- 可見性:持有鎖的線程修改共享資源后,釋放鎖時會將修改同步到主內存;其他線程獲取鎖時會從主內存讀取最新值(避免 “緩存一致性問題”)。
- 有序性:禁止指令重排序(避免多線程下的 “指令重排導致的邏輯錯亂”)。
二、Java 鎖的分類
(一)按實現(xiàn)方式:內置鎖 vs 顯式鎖
1. 內置鎖:synchronized(JVM 層面實現(xiàn))
synchronized 是 Java 原生的隱式鎖,基于 對象監(jiān)視器(Monitor) 實現(xiàn),無需手動釋放,簡單易用、線程安全。
(1)使用場景
(2)示例代碼
public class SynchronizedDemo {
// 1. 實例方法鎖(鎖:this)
public synchronized void method1() {
// 共享資源操作
}
// 2. 靜態(tài)方法鎖(鎖:SynchronizedDemo.class)
public static synchronized void method2() {
// 共享資源操作
}
// 3. 代碼塊鎖(鎖:自定義對象)
private final Object lock = new Object();
public void method3() {
synchronized (lock) {
// 共享資源操作
}
}
}(3)核心特性
(4)JVM 優(yōu)化:鎖升級(無鎖 → 偏向鎖 → 輕量級鎖 → 重量級鎖)
為了平衡性能和并發(fā)安全,JVM 對 synchronized 進行了鎖升級優(yōu)化(從低開銷到高開銷逐步過渡):
鎖升級是不可逆的(一旦升級為重量級鎖,不會降級)。
2. 顯式鎖:Lock 接口(Java 代碼層面實現(xiàn))
java.util.concurrent.locks.Lock 是 JDK 1.5 引入的顯式鎖,提供比 synchronized 更靈活的功能(如可中斷、超時獲取、公平鎖等),但需手動釋放鎖(否則會導致死鎖)。
(1)核心方法
| 方法 | 作用 |
|---|---|
lock() | 獲取鎖(阻塞,不可中斷) |
lockInterruptibly() | 獲取鎖(可被中斷,拋出 InterruptedException) |
tryLock() | 嘗試獲取鎖(非阻塞,成功返回 true,失敗返回 false) |
tryLock(long time, TimeUnit unit) | 超時獲取鎖(超時未獲取則返回 false) |
unlock() | 釋放鎖(必須在 finally 中調用) |
newCondition() | 創(chuàng)建條件變量(實現(xiàn)線程等待 / 喚醒) |
(2)常用實現(xiàn)類
(3)示例代碼(ReentrantLock)
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo {
// 創(chuàng)建非公平鎖(默認),若要公平鎖:new ReentrantLock(true)
private final ReentrantLock lock = new ReentrantLock();
public void method() {
// 1. 獲取鎖(必須手動獲?。?
lock.lock();
try {
// 共享資源操作
} finally {
// 2. 釋放鎖(必須在 finally 中釋放,避免鎖泄漏)
lock.unlock();
}
}
// 可中斷獲取鎖示例
public void interruptibleMethod() throws InterruptedException {
lock.lockInterruptibly(); // 線程可被中斷
try {
// 共享資源操作
} finally {
lock.unlock();
}
}
}(4)核心特性
(二)按功能特性分類
1. 可重入鎖 vs 不可重入鎖
2. 公平鎖 vs 非公平鎖
3. 讀寫鎖(ReentrantReadWriteLock)
針對 “讀多寫少” 場景優(yōu)化,分離讀鎖(共享鎖) 和寫鎖(排他鎖):
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReadWriteLockDemo {
private final ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
private final ReentrantReadWriteLock.ReadLock readLock = rwLock.readLock();
private final ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
private int data;
// 讀操作(共享鎖)
public int readData() {
readLock.lock();
try {
return data;
} finally {
readLock.unlock();
}
}
// 寫操作(排他鎖)
public void writeData(int value) {
writeLock.lock();
try {
data = value;
} finally {
writeLock.unlock();
}
}
}4. 樂觀鎖 vs 悲觀鎖
(三)其他特殊鎖
1. 自旋鎖(SpinLock)
線程獲取鎖失敗時,不立即阻塞,而是循環(huán)(自旋)嘗試獲取鎖(避免線程上下文切換開銷)。
2. 偏向鎖 / 輕量級鎖 / 重量級鎖(JVM 層面的鎖優(yōu)化)
見前文 synchronized 的鎖升級機制,核心是 “按需升級”,平衡性能和并發(fā)安全。
3. StampedLock(樂觀讀鎖)
JDK 1.8 引入,性能優(yōu)于 ReentrantReadWriteLock,支持三種模式:
三、synchronized vs ReentrantLock 對比
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 實現(xiàn)層面 | JVM 層面(底層是 Monitor) | Java 代碼層面(AQS 框架) |
| 可重入性 | 支持 | 支持 |
| 公平性 | 僅非公平鎖 | 公平 / 非公平可選 |
| 可中斷性 | 不支持 | 支持(lockInterruptibly ()) |
| 超時獲取 | 不支持 | 支持(tryLock (time)) |
| 條件變量 | 僅一個(wait ()/notify ()) | 多個(newCondition ()) |
| 鎖釋放 | 自動釋放(退出同步塊 / 方法) | 手動釋放(必須在 finally 中 unlock ()) |
| 性能 | JDK 1.6 后優(yōu)化(鎖升級),性能接近 ReentrantLock | 高(靈活控制) |
| 適用場景 | 簡單場景(無需復雜功能) | 復雜場景(需中斷、超時、公平鎖等) |
四、鎖的使用注意事項
總結
Java 鎖機制的核心是解決共享資源競爭,提供了從簡單到復雜的多種實現(xiàn):
使用鎖時需平衡線程安全和性能,避免死鎖、鎖粒度過大等問題,根據(jù)實際場景選擇合適的鎖類型。
最后祝各位開發(fā)者工作順利,萬事如意。
- 修飾實例方法:鎖是當前對象(
this)。 - 修飾靜態(tài)方法:鎖是當前類的
Class對象(全局鎖)。 - 修飾代碼塊:鎖是括號中的對象(可自定義鎖對象,靈活度更高)。
- 可重入:同一線程可多次獲取同一把鎖(避免死鎖,如遞歸調用 synchronized 方法)。
- 非公平鎖:線程獲取鎖的順序不按請求順序(默認,性能優(yōu)先)。
- 自動釋放:線程退出同步塊 / 方法時,無論正常退出還是異常退出,鎖都會自動釋放(無需手動處理)。
- 不可中斷:線程獲取鎖時若被阻塞,只能等待鎖釋放或一直阻塞(無法主動中斷)。
- 無鎖:無線程競爭時,無需加鎖(直接執(zhí)行)。
- 偏向鎖:單線程重復獲取鎖時,僅記錄線程 ID(避免 CAS 操作,開銷極低)。
- 觸發(fā)條件:只有一個線程訪問同步資源。
- 撤銷:當有第二個線程競爭時,偏向鎖會升級為輕量級鎖。
- 輕量級鎖: 多個線程交替訪問鎖(無激烈競爭),通過CAS(Compare and Swap)嘗試獲取鎖(自旋等待,避免阻塞線程)。
- 自旋次數(shù):默認 10 次(可通過
-XX:PreBlockSpin調整),自旋失敗則升級為重量級鎖。
- 自旋次數(shù):默認 10 次(可通過
- 重量級鎖:多個線程激烈競爭鎖(自旋無效),通過操作系統(tǒng)的 互斥量(Mutex) 實現(xiàn)(線程會阻塞,開銷最高)。
- ReentrantLock:可重入鎖(最常用),支持公平鎖和非公平鎖(默認非公平)。
- ReentrantReadWriteLock:讀寫鎖(分離讀鎖和寫鎖,提高讀多寫少場景的并發(fā)性能)。
- StampedLock:JDK 1.8 引入的樂觀讀鎖(性能優(yōu)于讀寫鎖,支持樂觀讀、寫鎖、悲觀讀鎖)。
- 可重入:與
synchronized一致,同一線程可多次獲取鎖(鎖計數(shù)器遞增,釋放時遞減至 0 才釋放)。 - 公平 / 非公平可選:構造函數(shù)傳入
true則為公平鎖(按線程請求順序分配鎖,性能略低),默認非公平鎖(性能優(yōu)先)。 - 可中斷:支持
lockInterruptibly()中斷阻塞中的線程(避免無限等待)。 - 超時獲取:通過
tryLock(time)避免線程永久阻塞。 - 條件變量:通過
newCondition()創(chuàng)建多個條件變量(比synchronized的wait()/notify()更靈活,可精準喚醒特定線程)。 可重入鎖:同一線程可多次獲取同一把鎖(如synchronized、ReentrantLock),避免遞歸調用或嵌套同步時的死鎖。
- 原理:鎖內部維護一個 “線程持有計數(shù)”,線程首次獲取時計數(shù)為 1,再次獲取時計數(shù)遞增,釋放時計數(shù)遞減,計數(shù)為 0 時鎖釋放。
不可重入鎖:同一線程無法多次獲取同一把鎖(如簡單的
SpinLock實現(xiàn)),容易導致死鎖,極少使用。- 公平鎖:線程獲取鎖的順序嚴格按照 “請求順序”(FIFO),不會出現(xiàn)饑餓(每個線程都能最終獲取鎖)。
- 實現(xiàn):維護一個等待隊列,新線程需加入隊列尾部,僅當隊列頭部線程才能獲取鎖。
- 缺點:性能較低(頻繁切換線程上下文)。
- 非公平鎖:線程獲取鎖時不按請求順序,允許 “插隊”(剛釋放鎖的線程可能再次獲取鎖,減少上下文切換)。
- 優(yōu)點:性能更高(默認選擇,如
synchronized、ReentrantLock默認)。 - 缺點:可能導致部分線程長期無法獲取鎖(饑餓)。
- 優(yōu)點:性能更高(默認選擇,如
- 讀鎖(共享鎖):多個線程可同時獲取讀鎖(讀操作不互斥)。
- 寫鎖(排他鎖):僅一個線程可獲取寫鎖(寫操作與讀 / 寫操作互斥)。
- 核心規(guī)則:讀鎖持有期間,寫鎖無法獲?。粚戞i持有期間,讀鎖和寫鎖均無法獲取。
- 示例代碼:
- 悲觀鎖:認為并發(fā)競爭必然發(fā)生,每次訪問共享資源都先加鎖(如synchronized、ReentrantLock)。
- 優(yōu)點:簡單可靠,適合寫操作多的場景。
- 缺點:阻塞線程,性能開銷高。
- 樂觀鎖:認為并發(fā)競爭很少發(fā)生,不加鎖直接訪問資源,僅在修改時通過 CAS 驗證是否有沖突(沖突則重試)。
- 實現(xiàn):
AtomicInteger(基于 CAS)、StampedLock(樂觀讀模式)、數(shù)據(jù)庫的版本號機制。 - 優(yōu)點:非阻塞,性能高,適合讀操作多的場景。
- 缺點:存在 “ABA 問題”(可通過版本號 / 時間戳解決),重試次數(shù)過多會消耗 CPU。
- 實現(xiàn):
- 適用場景:鎖持有時間短、并發(fā)度低的場景(如 JVM 輕量級鎖的自旋階段)。
- 缺點:自旋期間占用 CPU,若鎖持有時間長,會浪費 CPU 資源。
- 樂觀讀模式:無鎖訪問,讀取后驗證版本號(無沖突則成功,有沖突則升級為悲觀讀鎖)。
- 悲觀讀模式:共享鎖(類似讀寫鎖的讀鎖)。
- 寫模式:排他鎖(類似讀寫鎖的寫鎖)。
- 優(yōu)點:樂觀讀模式下無鎖競爭,性能極高;缺點:不支持可重入,使用復雜。
- 避免死鎖:
- 手動釋放鎖(ReentrantLock 必須在 finally 中 unlock ())。
- 避免嵌套鎖(或保證嵌套鎖的獲取順序一致)。
- 避免長時間持有鎖(減少鎖競爭)。
- 選擇合適的鎖類型:
- 讀多寫少:用
ReentrantReadWriteLock或StampedLock。 - 簡單場景:用
synchronized(無需手動管理鎖)。 - 復雜場景(中斷、超時、公平鎖):用
ReentrantLock。
- 讀多寫少:用
- 減少鎖粒度:
- 避免鎖覆蓋整個方法(僅對共享資源的操作加鎖,如 synchronized 代碼塊而非方法)。
- 示例:用
synchronized (lock)替代synchronized method()(僅鎖定關鍵代碼)。
- 避免鎖競爭:
- 無狀態(tài)對象無需加鎖(無共享資源)。
- 共享資源盡量設計為不可變(如
String、Integer)。 - 用局部變量替代共享變量(線程私有,無需鎖)。
- 基礎場景:優(yōu)先使用
synchronized(簡單、安全、JVM 優(yōu)化充分)。 - 復雜場景:使用
ReentrantLock(可中斷、超時、公平鎖)或ReentrantReadWriteLock(讀多寫少)。 - 性能優(yōu)化:利用 JVM 鎖升級(synchronized)、樂觀鎖(StampedLock、Atomic 類)減少鎖開銷。
到此這篇關于Java-并發(fā)鎖機制知識總結的文章就介紹到這了,更多相關Java并發(fā)鎖機制內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
SpringBoot 整合 Shiro 密碼登錄的實現(xiàn)代碼
這篇文章主要介紹了SpringBoot 整合 Shiro 密碼登錄的實現(xiàn),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02
springboot+vue實現(xiàn)阿里云oss上傳的示例代碼
文件上傳是常用的功能,本文主要介紹了springboot+vue實現(xiàn)阿里云oss上傳的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-06-06
Apache DolphinScheduler完全設置東八區(qū)時區(qū)
這篇文章主要為大家介紹了Apache DolphinScheduler完全設置東八區(qū)配置詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-11-11
SpringBoot項目刪除Bean或者不加載Bean的問題解決
文章介紹了在Spring Boot項目中如何使用@ComponentScan注解和自定義過濾器實現(xiàn)不加載某些Bean的方法,本文通過實例代碼給大家介紹的非常詳細,感興趣的朋友一起看看吧2025-01-01

