Synchronized
synchronized 是什麼
synchronized是 java 提供的原子性內置鎖,實現基本的同步機制,不支持超時,非公平,不可中斷,不支持多條件,基於 JVM 的 Monitor(監視鎖)機制實現,主要解決的是多個線程之間的訪問資源的同步性,可以保證被它修飾的方法或者代碼塊在任意時刻只有一個線程執行,以及保證:
- 原子性
- 可見性
- 有序性
監視器鎖(Monitor)是 JVM 內置的鎖機制,開發者無法直接操作 Monitor,只能通過
synchronized間接使用它。Monitor 的獲取與釋放對開發者是不可見的,由 JVM 自動管理
synchronized還是排它鎖,當一個線程獲得鎖之後,其他線程就必須等到該線程釋放鎖之後才能獲得鎖,由於java中的線程和操作系統原生線程一一對應,線程被阻塞或者喚醒的時候會從用户態轉換為內核態,這種轉換非常消耗性能。
synchronized 的可重入性
synchronized是可重入的,每獲取一次鎖,計數器加一,釋放鎖時,計數器減一,直到計數器為 0,鎖才會真正釋放。
ReentrantLock
ReentrantLock是 JUC(java.util.concurrent.locks)提供的一個可重入鎖、可中斷、公平鎖/非公平鎖任選的顯式鎖(Explicit Lock)
ReentrantLock 鎖模式
非公平鎖(默認)
獲取鎖的時候會“插隊”,性能高,吞吐量大
公平鎖
FIFO,先來先獲取鎖,但是性能比非公平鎖低
ReentrantLock lock = new ReentrantLock(true); // true = 公平鎖
ReentrantLock的能力
ReentrantLock底層實現主要是依賴於抽象類AbstractQueuedSynchronizer(AQS),該類提供了基本同步機制的框架,其中包含了隊列,狀態值等。
1、tryLock() – 嘗試獲得鎖,不等待
if (lock.tryLock()) {
try { ... } finally { lock.unlock(); }
} else {
System.out.println("獲取鎖失敗");
}
作用:防止線程永久等待 → 適合高性能場景(比如秒殺系統)
2、tryLock(timeout) – 超時獲取鎖
if (lock.tryLock(2, TimeUnit.SECONDS)) {
try { ... } finally { lock.unlock(); }
}
作用:避免長時間等待,適用於讀寫混用、高併發業務。
3、lockInterruptibly – 可中斷獲取鎖
try {
lock.lockInterruptibly();
try { ... } finally { lock.unlock(); }
} catch (InterruptedException e) {
System.out.println("線程被中斷,放棄等待鎖");
}
作用:在等待鎖期間可取消任務,適用於死鎖檢測等場景。
4、多條件隊列 – Condition
相比於synchronized只有一個wait-set,而ReentrantLock可以創建多個Condition
Condition condition = lock.newCondition();
作用:實現更復雜的線程通信(比如生產者 / 消費者 多條件控制)。
Synchronized VS. ReentrantLock
| 能力 | synchronized | ReentrantLock |
|---|---|---|
| 可重入 | ✔ | ✔ |
| 公平鎖 | ✘ | ✔(可選) |
| 非阻塞嘗試 | ✘ | tryLock() ✔ |
| 可中斷獲取鎖 | ✘ | lockInterruptibly() ✔ |
| 超時獲取鎖 | ✘ | tryLock(timeout) ✔ |
| 條件隊列 | 1 個 wait-set | 多個 Condition ✔ |
| 必須手動釋放鎖 | 自動 | 必須 unlock() |
| 使用場景 | 一般情況用Synchronized就行,比較簡單 | 比較靈活,支持的功能比較多,在複雜情況下用 |
volatile
volatile 的作用
主要保證變量的可見性和禁止指令重排優化,但是不能保證原子性
1、可見性(Visibility)
多個線程讀寫共享變量,如果不加 volatile:
- 線程可能讀取到 舊值(因為線程讀的是工作內存副本)
- volatile 讓線程每次讀取都從 主內存 讀
避免線程間由於緩存一致性問題導致的 “看見” 舊值的現象。
2、禁止指令重排序(ordering)
volatile 會插入內存屏障(Memory Barrier),例如:
- LoadLoad
- StoreStore
- StoreLoad(最強)
從而阻止 JVM 和 CPU 進行重排序
Synchronized VS. volatile
volatile 只保證可見性 + 禁止重排;synchronized 保證原子性 + 可見性 + 有序性。
volatile 是“輕量級讀寫”;synchronized 是“重量級加鎖”。
| 對比項 | volatile | synchronized |
|---|---|---|
| 是否保證原子性 | ❌ 不保證 | ✔ 保證 |
| 可見性 | ✔ 保證 | ✔ 保證 |
| 是否禁止指令重排 | ✔ 禁止 | ✔ 禁止(通過內存屏障) |
| 是否會阻塞線程 | ❌ 不會阻塞 | ✔ 可能阻塞(等待鎖) |
| 是否適用於複合操作(i++) | ❌ 不適用 | ✔ 適用 |
| 性能 | ⭐ 非常快 | 🐢 慢(涉及鎖競爭) |
| 底層實現 | 內存屏障 + volatile 寫入協議 | 監視器鎖(Monitorenter/monitorexit) |
| 是否可重入 | 不適用 | ✔ 可重入鎖 |
| 是否能實現臨界區保護 | ❌ 不行 | ✔ 可以 |
| 適用場景 | 狀態標誌、DCL 單例、配置刷新 | 多線程共享修改的臨界區 |
The end….