Java并發(fā)鎖機(jī)制知識(shí)總結(jié)(最新整理)
并發(fā)鎖機(jī)制知識(shí)總結(jié)
Java 的鎖機(jī)制是并發(fā)編程的核心,用于解決多線程共享資源競爭問題,保證數(shù)據(jù)一致性和線程安全。Java 提供了內(nèi)置鎖(synchronized)、顯式鎖(Lock 接口) 等多種鎖實(shí)現(xiàn),同時(shí)衍生出偏向鎖、輕量級鎖、重量級鎖等優(yōu)化機(jī)制,以及可重入鎖、公平鎖、讀寫鎖等功能分類。
一、鎖的核心作用
- 互斥性:同一時(shí)間只有一個(gè)線程能持有鎖,訪問共享資源(解決 “競態(tài)條件”)。
- 可見性:持有鎖的線程修改共享資源后,釋放鎖時(shí)會(huì)將修改同步到主內(nèi)存;其他線程獲取鎖時(shí)會(huì)從主內(nèi)存讀取最新值(避免 “緩存一致性問題”)。
- 有序性:禁止指令重排序(避免多線程下的 “指令重排導(dǎo)致的邏輯錯(cuò)亂”)。
二、Java 鎖的分類
(一)按實(shí)現(xiàn)方式:內(nèi)置鎖 vs 顯式鎖
1. 內(nèi)置鎖:synchronized(JVM 層面實(shí)現(xiàn))
synchronized 是 Java 原生的隱式鎖,基于 對象監(jiān)視器(Monitor) 實(shí)現(xiàn),無需手動(dòng)釋放,簡單易用、線程安全。
(1)使用場景
(2)示例代碼
public class SynchronizedDemo {
// 1. 實(shí)例方法鎖(鎖: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 進(jìn)行了鎖升級優(yōu)化(從低開銷到高開銷逐步過渡):
鎖升級是不可逆的(一旦升級為重量級鎖,不會(huì)降級)。
2. 顯式鎖:Lock 接口(Java 代碼層面實(shí)現(xiàn))
java.util.concurrent.locks.Lock 是 JDK 1.5 引入的顯式鎖,提供比 synchronized 更靈活的功能(如可中斷、超時(shí)獲取、公平鎖等),但需手動(dòng)釋放鎖(否則會(huì)導(dǎo)致死鎖)。
(1)核心方法
| 方法 | 作用 |
|---|---|
lock() | 獲取鎖(阻塞,不可中斷) |
lockInterruptibly() | 獲取鎖(可被中斷,拋出 InterruptedException) |
tryLock() | 嘗試獲取鎖(非阻塞,成功返回 true,失敗返回 false) |
tryLock(long time, TimeUnit unit) | 超時(shí)獲取鎖(超時(shí)未獲取則返回 false) |
unlock() | 釋放鎖(必須在 finally 中調(diào)用) |
newCondition() | 創(chuàng)建條件變量(實(shí)現(xiàn)線程等待 / 喚醒) |
(2)常用實(shí)現(xiàn)類
(3)示例代碼(ReentrantLock)
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo {
// 創(chuàng)建非公平鎖(默認(rèn)),若要公平鎖:new ReentrantLock(true)
private final ReentrantLock lock = new ReentrantLock();
public void method() {
// 1. 獲取鎖(必須手動(dòng)獲?。?
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)
線程獲取鎖失敗時(shí),不立即阻塞,而是循環(huán)(自旋)嘗試獲取鎖(避免線程上下文切換開銷)。
2. 偏向鎖 / 輕量級鎖 / 重量級鎖(JVM 層面的鎖優(yōu)化)
見前文 synchronized 的鎖升級機(jī)制,核心是 “按需升級”,平衡性能和并發(fā)安全。
3. StampedLock(樂觀讀鎖)
JDK 1.8 引入,性能優(yōu)于 ReentrantReadWriteLock,支持三種模式:
三、synchronized vs ReentrantLock 對比
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 實(shí)現(xiàn)層面 | JVM 層面(底層是 Monitor) | Java 代碼層面(AQS 框架) |
| 可重入性 | 支持 | 支持 |
| 公平性 | 僅非公平鎖 | 公平 / 非公平可選 |
| 可中斷性 | 不支持 | 支持(lockInterruptibly ()) |
| 超時(shí)獲取 | 不支持 | 支持(tryLock (time)) |
| 條件變量 | 僅一個(gè)(wait ()/notify ()) | 多個(gè)(newCondition ()) |
| 鎖釋放 | 自動(dòng)釋放(退出同步塊 / 方法) | 手動(dòng)釋放(必須在 finally 中 unlock ()) |
| 性能 | JDK 1.6 后優(yōu)化(鎖升級),性能接近 ReentrantLock | 高(靈活控制) |
| 適用場景 | 簡單場景(無需復(fù)雜功能) | 復(fù)雜場景(需中斷、超時(shí)、公平鎖等) |
四、鎖的使用注意事項(xiàng)
總結(jié)
Java 鎖機(jī)制的核心是解決共享資源競爭,提供了從簡單到復(fù)雜的多種實(shí)現(xiàn):
使用鎖時(shí)需平衡線程安全和性能,避免死鎖、鎖粒度過大等問題,根據(jù)實(shí)際場景選擇合適的鎖類型。
最后祝各位開發(fā)者工作順利,萬事如意。
- 修飾實(shí)例方法:鎖是當(dāng)前對象(
this)。 - 修飾靜態(tài)方法:鎖是當(dāng)前類的
Class對象(全局鎖)。 - 修飾代碼塊:鎖是括號(hào)中的對象(可自定義鎖對象,靈活度更高)。
- 可重入:同一線程可多次獲取同一把鎖(避免死鎖,如遞歸調(diào)用 synchronized 方法)。
- 非公平鎖:線程獲取鎖的順序不按請求順序(默認(rèn),性能優(yōu)先)。
- 自動(dòng)釋放:線程退出同步塊 / 方法時(shí),無論正常退出還是異常退出,鎖都會(huì)自動(dòng)釋放(無需手動(dòng)處理)。
- 不可中斷:線程獲取鎖時(shí)若被阻塞,只能等待鎖釋放或一直阻塞(無法主動(dòng)中斷)。
- 無鎖:無線程競爭時(shí),無需加鎖(直接執(zhí)行)。
- 偏向鎖:單線程重復(fù)獲取鎖時(shí),僅記錄線程 ID(避免 CAS 操作,開銷極低)。
- 觸發(fā)條件:只有一個(gè)線程訪問同步資源。
- 撤銷:當(dāng)有第二個(gè)線程競爭時(shí),偏向鎖會(huì)升級為輕量級鎖。
- 輕量級鎖: 多個(gè)線程交替訪問鎖(無激烈競爭),通過CAS(Compare and Swap)嘗試獲取鎖(自旋等待,避免阻塞線程)。
- 自旋次數(shù):默認(rèn) 10 次(可通過
-XX:PreBlockSpin調(diào)整),自旋失敗則升級為重量級鎖。
- 自旋次數(shù):默認(rèn) 10 次(可通過
- 重量級鎖:多個(gè)線程激烈競爭鎖(自旋無效),通過操作系統(tǒng)的 互斥量(Mutex) 實(shí)現(xiàn)(線程會(huì)阻塞,開銷最高)。
- ReentrantLock:可重入鎖(最常用),支持公平鎖和非公平鎖(默認(rèn)非公平)。
- ReentrantReadWriteLock:讀寫鎖(分離讀鎖和寫鎖,提高讀多寫少場景的并發(fā)性能)。
- StampedLock:JDK 1.8 引入的樂觀讀鎖(性能優(yōu)于讀寫鎖,支持樂觀讀、寫鎖、悲觀讀鎖)。
- 可重入:與
synchronized一致,同一線程可多次獲取鎖(鎖計(jì)數(shù)器遞增,釋放時(shí)遞減至 0 才釋放)。 - 公平 / 非公平可選:構(gòu)造函數(shù)傳入
true則為公平鎖(按線程請求順序分配鎖,性能略低),默認(rèn)非公平鎖(性能優(yōu)先)。 - 可中斷:支持
lockInterruptibly()中斷阻塞中的線程(避免無限等待)。 - 超時(shí)獲取:通過
tryLock(time)避免線程永久阻塞。 - 條件變量:通過
newCondition()創(chuàng)建多個(gè)條件變量(比synchronized的wait()/notify()更靈活,可精準(zhǔn)喚醒特定線程)。 可重入鎖:同一線程可多次獲取同一把鎖(如synchronized、ReentrantLock),避免遞歸調(diào)用或嵌套同步時(shí)的死鎖。
- 原理:鎖內(nèi)部維護(hù)一個(gè) “線程持有計(jì)數(shù)”,線程首次獲取時(shí)計(jì)數(shù)為 1,再次獲取時(shí)計(jì)數(shù)遞增,釋放時(shí)計(jì)數(shù)遞減,計(jì)數(shù)為 0 時(shí)鎖釋放。
不可重入鎖:同一線程無法多次獲取同一把鎖(如簡單的
SpinLock實(shí)現(xiàn)),容易導(dǎo)致死鎖,極少使用。- 公平鎖:線程獲取鎖的順序嚴(yán)格按照 “請求順序”(FIFO),不會(huì)出現(xiàn)饑餓(每個(gè)線程都能最終獲取鎖)。
- 實(shí)現(xiàn):維護(hù)一個(gè)等待隊(duì)列,新線程需加入隊(duì)列尾部,僅當(dāng)隊(duì)列頭部線程才能獲取鎖。
- 缺點(diǎn):性能較低(頻繁切換線程上下文)。
- 非公平鎖:線程獲取鎖時(shí)不按請求順序,允許 “插隊(duì)”(剛釋放鎖的線程可能再次獲取鎖,減少上下文切換)。
- 優(yōu)點(diǎn):性能更高(默認(rèn)選擇,如
synchronized、ReentrantLock默認(rèn))。 - 缺點(diǎn):可能導(dǎo)致部分線程長期無法獲取鎖(饑餓)。
- 優(yōu)點(diǎn):性能更高(默認(rèn)選擇,如
- 讀鎖(共享鎖):多個(gè)線程可同時(shí)獲取讀鎖(讀操作不互斥)。
- 寫鎖(排他鎖):僅一個(gè)線程可獲取寫鎖(寫操作與讀 / 寫操作互斥)。
- 核心規(guī)則:讀鎖持有期間,寫鎖無法獲??;寫鎖持有期間,讀鎖和寫鎖均無法獲取。
- 示例代碼:
- 悲觀鎖:認(rèn)為并發(fā)競爭必然發(fā)生,每次訪問共享資源都先加鎖(如synchronized、ReentrantLock)。
- 優(yōu)點(diǎn):簡單可靠,適合寫操作多的場景。
- 缺點(diǎn):阻塞線程,性能開銷高。
- 樂觀鎖:認(rèn)為并發(fā)競爭很少發(fā)生,不加鎖直接訪問資源,僅在修改時(shí)通過 CAS 驗(yàn)證是否有沖突(沖突則重試)。
- 實(shí)現(xiàn):
AtomicInteger(基于 CAS)、StampedLock(樂觀讀模式)、數(shù)據(jù)庫的版本號(hào)機(jī)制。 - 優(yōu)點(diǎn):非阻塞,性能高,適合讀操作多的場景。
- 缺點(diǎn):存在 “ABA 問題”(可通過版本號(hào) / 時(shí)間戳解決),重試次數(shù)過多會(huì)消耗 CPU。
- 實(shí)現(xiàn):
- 適用場景:鎖持有時(shí)間短、并發(fā)度低的場景(如 JVM 輕量級鎖的自旋階段)。
- 缺點(diǎn):自旋期間占用 CPU,若鎖持有時(shí)間長,會(huì)浪費(fèi) CPU 資源。
- 樂觀讀模式:無鎖訪問,讀取后驗(yàn)證版本號(hào)(無沖突則成功,有沖突則升級為悲觀讀鎖)。
- 悲觀讀模式:共享鎖(類似讀寫鎖的讀鎖)。
- 寫模式:排他鎖(類似讀寫鎖的寫鎖)。
- 優(yōu)點(diǎn):樂觀讀模式下無鎖競爭,性能極高;缺點(diǎn):不支持可重入,使用復(fù)雜。
- 避免死鎖:
- 手動(dòng)釋放鎖(ReentrantLock 必須在 finally 中 unlock ())。
- 避免嵌套鎖(或保證嵌套鎖的獲取順序一致)。
- 避免長時(shí)間持有鎖(減少鎖競爭)。
- 選擇合適的鎖類型:
- 讀多寫少:用
ReentrantReadWriteLock或StampedLock。 - 簡單場景:用
synchronized(無需手動(dòng)管理鎖)。 - 復(fù)雜場景(中斷、超時(shí)、公平鎖):用
ReentrantLock。
- 讀多寫少:用
- 減少鎖粒度:
- 避免鎖覆蓋整個(gè)方法(僅對共享資源的操作加鎖,如 synchronized 代碼塊而非方法)。
- 示例:用
synchronized (lock)替代synchronized method()(僅鎖定關(guān)鍵代碼)。
- 避免鎖競爭:
- 無狀態(tài)對象無需加鎖(無共享資源)。
- 共享資源盡量設(shè)計(jì)為不可變(如
String、Integer)。 - 用局部變量替代共享變量(線程私有,無需鎖)。
- 基礎(chǔ)場景:優(yōu)先使用
synchronized(簡單、安全、JVM 優(yōu)化充分)。 - 復(fù)雜場景:使用
ReentrantLock(可中斷、超時(shí)、公平鎖)或ReentrantReadWriteLock(讀多寫少)。 - 性能優(yōu)化:利用 JVM 鎖升級(synchronized)、樂觀鎖(StampedLock、Atomic 類)減少鎖開銷。
到此這篇關(guān)于Java-并發(fā)鎖機(jī)制知識(shí)總結(jié)的文章就介紹到這了,更多相關(guān)Java并發(fā)鎖機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java微信公眾平臺(tái)開發(fā)(1) 接入微信公眾平臺(tái)
這篇文章主要為大家詳細(xì)介紹了Java微信公眾平臺(tái)開發(fā)第一步,接入微信公眾平臺(tái),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04
Java RestTemplate post請求參數(shù)問題及解決
作者解決了用JavaRestTemplate進(jìn)行POST請求時(shí)遇到的參數(shù)傳遞問題,發(fā)現(xiàn)使用MultiValueMap比Map更合適,同時(shí)強(qiáng)調(diào)了請求頭的數(shù)據(jù)類型和URL的占位符在GET請求中如何正確使用2025-12-12
Java使用Fastjson2處理JSON字段大小寫不一致的優(yōu)雅方案
這篇文章主要為大家詳細(xì)介紹了Java使用Fastjson2處理JSON字段大小寫不一致的優(yōu)雅方案,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2026-04-04

