Java?中的?synchronized?關(guān)鍵字詳解
一、synchronized 關(guān)鍵字的基礎(chǔ)認(rèn)知
1.1 什么是 synchronized?
synchronized 是 Java 中用于實(shí)現(xiàn)線程同步的關(guān)鍵字,它提供了內(nèi)置的鎖機(jī)制來協(xié)調(diào)多線程對(duì)共享資源的訪問。在 Java 并發(fā)編程中,synchronized 是最基本、最常用的同步手段之一。
工作原理
synchronized 通過對(duì)象監(jiān)視器(Monitor)機(jī)制實(shí)現(xiàn)同步。當(dāng)線程進(jìn)入 synchronized 代碼塊時(shí):
- 首先嘗試獲取對(duì)象的監(jiān)視器鎖(Monitor Lock)
- 如果鎖可用,線程獲取鎖并進(jìn)入同步塊
- 如果鎖被其他線程持有,當(dāng)前線程進(jìn)入阻塞狀態(tài),直到鎖被釋放
- 線程執(zhí)行完同步代碼后自動(dòng)釋放鎖
鎖的存儲(chǔ)位置
在 JVM 中,每個(gè)對(duì)象都有一個(gè)對(duì)象頭(Object Header),其中包含兩部分重要信息:
- Mark Word:存儲(chǔ)對(duì)象的哈希碼、GC 分代年齡等信息
- Klass Pointer:指向?qū)ο箢愋蛿?shù)據(jù)的指針
- 如果是數(shù)組對(duì)象,還會(huì)有數(shù)組長(zhǎng)度信息
synchronized 使用的鎖信息就存儲(chǔ)在 Mark Word 中,包括:
- 鎖狀態(tài)標(biāo)志位
- 指向鎖記錄的指針
- 指向重量級(jí)鎖的指針
- 偏向線程 ID 等
1.2 synchronized 的核心作用
1.2.1 原子性保障
原子性是指一個(gè)操作不可中斷,要么全部執(zhí)行成功,要么全部不執(zhí)行。synchronized 通過互斥鎖機(jī)制保證被其修飾的代碼塊的原子性執(zhí)行。
典型應(yīng)用場(chǎng)景:
public class Counter {
private int count = 0;
public synchronized void increment() {
count++; // 非原子操作變?yōu)樵硬僮?
}
}在這個(gè)例子中,count++ 操作實(shí)際上包含三個(gè)步驟:
- 讀取 count 的值
- 將值加 1
- 寫回新值 如果沒有 synchronized,多線程環(huán)境下可能導(dǎo)致更新丟失。
1.2.2 可見性保障
可見性是指當(dāng)一個(gè)線程修改了共享變量的值后,其他線程能夠立即看到修改后的值。synchronized 通過以下機(jī)制保證可見性:
- 線程獲取鎖時(shí):
- 清空工作內(nèi)存(本地內(nèi)存)中的變量副本
- 從主內(nèi)存重新讀取最新值
- 線程釋放鎖時(shí):
- 將工作內(nèi)存中修改過的變量刷新到主內(nèi)存
- 確保其他線程能看到最新修改
這符合 JMM(Java 內(nèi)存模型)的 happens-before 原則中的監(jiān)視器鎖規(guī)則:對(duì)一個(gè)鎖的解鎖 happens-before 于隨后對(duì)這個(gè)鎖的加鎖。
1.2.3 有序性保障
有序性是指程序執(zhí)行的順序按照代碼的先后順序執(zhí)行。synchronized 通過以下方式保證有序性:
- 禁止指令重排序:編譯器不會(huì)對(duì)同步塊內(nèi)的指令進(jìn)行重排序優(yōu)化
- 建立內(nèi)存屏障(Memory Barrier):確保同步塊內(nèi)的指令不會(huì)"逃逸"到同步塊之外執(zhí)行
示例:
public class Singleton {
private static Singleton instance;
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton(); // 保證new操作的原子性和有序性
}
return instance;
}
}二、synchronized 的使用場(chǎng)景及語法詳解
synchronized 是 Java 中最基本的同步機(jī)制,用于解決多線程環(huán)境下的線程安全問題。它主要有三種使用方式,每種方式對(duì)應(yīng)不同的鎖對(duì)象和同步范圍,適用于不同的并發(fā)場(chǎng)景。
2.1 修飾實(shí)例方法
詳細(xì)說明
當(dāng) synchronized 修飾實(shí)例方法時(shí),鎖對(duì)象是當(dāng)前類的實(shí)例對(duì)象(即 this 對(duì)象)。這種同步方式適用于需要保護(hù)實(shí)例變量的場(chǎng)景,如:
- 銀行賬戶的余額操作
- 購物車的商品數(shù)量修改
- 計(jì)數(shù)器對(duì)象的自增操作
典型應(yīng)用場(chǎng)景
假設(shè)我們有一個(gè)銀行賬戶類,需要保證多線程環(huán)境下存款操作的原子性:
public class BankAccount {
private double balance;
// 使用synchronized修飾實(shí)例方法保證線程安全
public synchronized void deposit(double amount) {
double newBalance = balance + amount;
try {
// 模擬耗時(shí)操作
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
balance = newBalance;
}
public synchronized double getBalance() {
return balance;
}
}注意事項(xiàng)
- 這種同步方式只對(duì)同一個(gè)實(shí)例對(duì)象的方法調(diào)用有效
- 不同實(shí)例對(duì)象的方法調(diào)用不會(huì)互斥
- 方法執(zhí)行期間會(huì)阻塞其他線程調(diào)用同一實(shí)例的同步方法
- 應(yīng)避免在同步方法中執(zhí)行長(zhǎng)時(shí)間操作,以免影響性能
鎖范圍示意圖
[實(shí)例對(duì)象A] ├── 同步方法1 (鎖A) └── 同步方法2 (鎖A) [實(shí)例對(duì)象B] ├── 同步方法1 (鎖B) └── 同步方法2 (鎖B)
2.2 修飾靜態(tài)方法
詳細(xì)說明
當(dāng) synchronized 修飾靜態(tài)方法時(shí),鎖對(duì)象是當(dāng)前類的 Class 對(duì)象。這種同步方式適用于:
- 靜態(tài)變量的操作
- 類級(jí)別的共享資源訪問
- 單例模式的實(shí)現(xiàn)
典型應(yīng)用場(chǎng)景
例如,我們需要一個(gè)全局計(jì)數(shù)器來統(tǒng)計(jì)所有實(shí)例的創(chuàng)建次數(shù):
public class InstanceCounter {
private static int count = 0;
// 靜態(tài)同步方法保證類變量的線程安全
public static synchronized void increment() {
count++;
}
public static synchronized int getCount() {
return count;
}
public InstanceCounter() {
increment();
}
}執(zhí)行特點(diǎn)
- 無論創(chuàng)建多少個(gè)實(shí)例對(duì)象,靜態(tài)同步方法都會(huì)互斥執(zhí)行
- 類加載時(shí)就會(huì)初始化 Class 對(duì)象,全局唯一
- 靜態(tài)同步方法和實(shí)例同步方法使用不同的鎖,不會(huì)互相阻塞
鎖范圍示意圖
[Class對(duì)象] └── 靜態(tài)同步方法 (鎖Class) [實(shí)例對(duì)象A] [實(shí)例對(duì)象B] ...
2.3 修飾代碼塊
詳細(xì)說明
synchronized 代碼塊是最靈活的同步方式,可以顯式指定鎖對(duì)象,適用于:
- 只需要同步部分代碼而非整個(gè)方法
- 需要細(xì)粒度控制同步范圍
- 使用非this對(duì)象作為鎖的情況
三種常見用法詳解
1. 使用 this 作為鎖對(duì)象
public void doSomething() {
// 非同步代碼
System.out.println("非同步代碼");
// 同步代碼塊
synchronized (this) {
// 需要同步的代碼
System.out.println("同步代碼塊");
}
}特點(diǎn):等同于同步實(shí)例方法,但可以只同步部分代碼
2. 使用 Class 對(duì)象作為鎖對(duì)象
public void doSomething() {
synchronized (MyClass.class) {
// 需要同步的靜態(tài)資源操作
System.out.println("同步靜態(tài)資源");
}
}
特點(diǎn):等同于靜態(tài)同步方法,但可以只在需要的地方加鎖
3. 使用自定義對(duì)象作為鎖對(duì)象
private final Object lock = new Object();
public void doSomething() {
synchronized (lock) {
// 需要同步的代碼
System.out.println("使用自定義鎖");
}
}優(yōu)勢(shì):
- 可以創(chuàng)建多個(gè)鎖對(duì)象實(shí)現(xiàn)更細(xì)粒度的控制
- 避免直接使用this或Class對(duì)象可能導(dǎo)致的問題
- 鎖對(duì)象通常聲明為final防止意外修改
典型應(yīng)用場(chǎng)景:雙重檢查鎖定單例模式
public class Singleton {
private static volatile Singleton instance;
private static final Object lock = new Object();
public static Singleton getInstance() {
if (instance == null) {
synchronized (lock) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}性能優(yōu)化建議
- 盡量減小同步代碼塊的范圍
- 避免在同步塊中調(diào)用耗時(shí)操作(如IO)
- 對(duì)于讀多寫少的場(chǎng)景,考慮使用讀寫鎖
- 不同功能使用不同鎖對(duì)象,減少鎖競(jìng)爭(zhēng)
鎖選擇策略
| 鎖類型 | 適用場(chǎng)景 | 優(yōu)點(diǎn) | 缺點(diǎn) |
|---|---|---|---|
| 實(shí)例鎖 | 保護(hù)實(shí)例變量 | 簡(jiǎn)單直接 | 粒度較粗 |
| 類鎖 | 保護(hù)靜態(tài)變量 | 全局控制 | 可能影響性能 |
| 代碼塊鎖 | 需要細(xì)粒度控制 | 靈活高效 | 實(shí)現(xiàn)較復(fù)雜 |
三、synchronized 的底層實(shí)現(xiàn)原理
要深入理解 synchronized 的工作機(jī)制,需要從 JVM 底層實(shí)現(xiàn)進(jìn)行分析。在 JDK 1.6 之前,synchronized 被稱為"重量級(jí)鎖",因?yàn)樗膶?shí)現(xiàn)完全依賴于操作系統(tǒng)的互斥量(Mutex)機(jī)制,每次線程的阻塞和喚醒都需要在用戶態(tài)和內(nèi)核態(tài)之間切換,帶來較大的性能開銷。根據(jù)測(cè)試數(shù)據(jù),這種上下文切換的開銷可能達(dá)到幾十微秒級(jí)別,對(duì)于高并發(fā)場(chǎng)景影響顯著。
JDK 1.6 及之后的版本中,HotSpot 虛擬機(jī)團(tuán)隊(duì)對(duì) synchronized 進(jìn)行了重大優(yōu)化,引入了全新的鎖升級(jí)機(jī)制,包括偏向鎖、輕量級(jí)鎖等概念,使其性能得到極大提升。在大多數(shù)低競(jìng)爭(zhēng)場(chǎng)景下,性能表現(xiàn)甚至可以與 java.util.concurrent 包中的顯式鎖相媲美。
3.1 對(duì)象頭:鎖的存儲(chǔ)載體
在 Java 對(duì)象的內(nèi)存布局中,每個(gè)對(duì)象都包含以下三部分:
- 對(duì)象頭(Object Header):存儲(chǔ)對(duì)象運(yùn)行時(shí)數(shù)據(jù)
- 實(shí)例數(shù)據(jù)(Instance Data):存儲(chǔ)對(duì)象的實(shí)際字段信息
- 對(duì)齊填充(Padding):為了字節(jié)對(duì)齊而存在的填充數(shù)據(jù)
以 32 位 JVM 為例,普通對(duì)象的對(duì)象頭結(jié)構(gòu)如下表所示:
| 長(zhǎng)度(bit) | 內(nèi)容 | 詳細(xì)說明 |
|---|---|---|
| 25 | 哈希碼(HashCode) | 對(duì)象的哈希值,用于哈希表等數(shù)據(jù)結(jié)構(gòu) |
| 4 | GC 分代年齡 | 記錄對(duì)象被 GC 標(biāo)記的次數(shù),達(dá)到閾值(默認(rèn)15)會(huì)被移入老年代 |
| 1 | 是否偏向鎖(biased_lock) | 0表示非偏向鎖,1表示偏向鎖 |
| 2 | 鎖狀態(tài)標(biāo)志(lock) | 01:無鎖;00:輕量級(jí)鎖;10:重量級(jí)鎖;11:GC標(biāo)記 |
| 2 | (數(shù)組對(duì)象)數(shù)組長(zhǎng)度 | 僅數(shù)組對(duì)象擁有,記錄數(shù)組長(zhǎng)度 |
當(dāng)對(duì)象作為 synchronized 的鎖對(duì)象時(shí),JVM 主要通過修改對(duì)象頭中的"鎖狀態(tài)標(biāo)志"和"是否偏向鎖"字段來記錄鎖狀態(tài)。這里需要注意,在 64 位 JVM 中,對(duì)象頭的結(jié)構(gòu)會(huì)有所不同,但基本原理相同。
3.2 鎖的升級(jí)過程:從偏向鎖到重量級(jí)鎖
JDK 1.6 引入的鎖升級(jí)機(jī)制是 synchronized 性能優(yōu)化的核心,其升級(jí)路徑為: 無鎖 → 偏向鎖 → 輕量級(jí)鎖 → 重量級(jí)鎖 這個(gè)過程是單向的,一旦升級(jí)到重量級(jí)鎖就不會(huì)再降級(jí)。
3.2.1 無鎖狀態(tài)(Lock-Free)
初始狀態(tài)下,對(duì)象處于無鎖狀態(tài):
- 鎖狀態(tài)標(biāo)志:01
- 是否偏向鎖:0
此時(shí)任何線程都可以通過 CAS 操作嘗試獲取鎖。
3.2.2 偏向鎖(Biased Locking)
設(shè)計(jì)背景:統(tǒng)計(jì)表明,大多數(shù)情況下鎖不僅不存在多線程競(jìng)爭(zhēng),而且總是由同一個(gè)線程多次獲得。例如 GUI 應(yīng)用中的事件分發(fā)線程、消息隊(duì)列的消費(fèi)線程等。
工作流程:
- 首次獲取鎖時(shí),JVM 通過 CAS 操作將:
- 是否偏向鎖置為1
- 鎖狀態(tài)標(biāo)志保持01
- 將當(dāng)前線程ID寫入對(duì)象頭
- 之后同一線程再次獲取鎖時(shí),只需簡(jiǎn)單檢查線程ID即可,無需任何同步操作
- 當(dāng)其他線程嘗試獲取鎖時(shí),偏向模式立即結(jié)束,可能升級(jí)為輕量級(jí)鎖
性能優(yōu)勢(shì):完全避免了同步操作,獲取鎖的代價(jià)降低到只需一次內(nèi)存訪問。
3.2.3 輕量級(jí)鎖(Lightweight Locking)
適用場(chǎng)景:多個(gè)線程交替執(zhí)行同步塊,但基本不會(huì)同時(shí)競(jìng)爭(zhēng)鎖的情況。例如生產(chǎn)者-消費(fèi)者模式中生產(chǎn)者和消費(fèi)者線程的交替執(zhí)行。
詳細(xì)工作流程:
- 在棧幀中創(chuàng)建鎖記錄(Lock Record),包含:
- Displaced Mark Word:對(duì)象頭原始內(nèi)容的拷貝
- 指向鎖對(duì)象的指針
- 通過 CAS 操作嘗試將對(duì)象頭的 Mark Word 替換為指向鎖記錄的指針
- 如果成功:
- 鎖狀態(tài)標(biāo)志變?yōu)?0
- 線程獲得輕量級(jí)鎖
- 如果失?。?ul>
- 檢查是否當(dāng)前線程已持有鎖(重入情況)
- 否則開始自旋等待(自適應(yīng)自旋)
- 自旋失敗后升級(jí)為重量級(jí)鎖
自旋優(yōu)化:JVM 采用自適應(yīng)自旋策略,會(huì)根據(jù)之前自旋的成功率動(dòng)態(tài)調(diào)整自旋次數(shù)。
3.2.4 重量級(jí)鎖(Heavyweight Locking)
核心組件:
- 監(jiān)視器(Monitor):每個(gè)Java對(duì)象都關(guān)聯(lián)一個(gè)Monitor
- 等待隊(duì)列(Entry Set):存放等待獲取鎖的線程
- 等待池(Wait Set):存放調(diào)用了wait()的線程
工作流程:
- 當(dāng)鎖升級(jí)為重量級(jí)鎖后:
- 鎖狀態(tài)標(biāo)志變?yōu)?0
- Mark Word 指向Monitor對(duì)象
- 線程競(jìng)爭(zhēng)鎖時(shí):
- 成功則執(zhí)行同步代碼
- 失敗則進(jìn)入阻塞狀態(tài),加入等待隊(duì)列
- 鎖釋放時(shí):
- 喚醒等待隊(duì)列中的線程
- 被喚醒線程需要重新競(jìng)爭(zhēng)鎖
性能特點(diǎn):
- 線程阻塞和喚醒涉及操作系統(tǒng)內(nèi)核操作
- 上下文切換開銷較大(約5-10μs)
- 適合鎖競(jìng)爭(zhēng)激烈且持有時(shí)間長(zhǎng)的場(chǎng)景
鎖升級(jí)過程示例
假設(shè)有一個(gè)同步方法:
public synchronized void increment() {
count++;
}
- 初始調(diào)用(線程A):無鎖 → 偏向鎖,對(duì)象頭記錄線程A ID
- 線程A再次調(diào)用:直接通過偏向鎖訪問
- 線程B首次調(diào)用:撤銷偏向鎖 → 輕量級(jí)鎖,線程B通過CAS獲取鎖
- 線程A和B交替調(diào)用:維持輕量級(jí)鎖狀態(tài)
- 線程A和B同時(shí)競(jìng)爭(zhēng):輕量級(jí)鎖 → 重量級(jí)鎖,線程阻塞,進(jìn)入等待隊(duì)列
這種漸進(jìn)式的鎖升級(jí)策略,使得 synchronized 在各種競(jìng)爭(zhēng)程度下都能保持較好的性能表現(xiàn)。
四、synchronized 的性能優(yōu)化技巧
4.1 減小鎖粒度:拆分鎖對(duì)象,降低競(jìng)爭(zhēng)頻率
核心原理
通過將一個(gè)大鎖拆分為多個(gè)小鎖,允許不同線程同時(shí)訪問不同的資源分區(qū),從而提升系統(tǒng)吞吐量。這種技術(shù)特別適用于讀多寫少的場(chǎng)景,或者在數(shù)據(jù)可以自然分片的場(chǎng)景中。
實(shí)現(xiàn)細(xì)節(jié)
- 分區(qū)策略:通常采用哈希算法將數(shù)據(jù)分配到不同的分區(qū),確保數(shù)據(jù)分布均勻
- 鎖數(shù)量:一般設(shè)置為2的冪次方,便于位運(yùn)算優(yōu)化
- 擴(kuò)容處理:需要考慮動(dòng)態(tài)擴(kuò)容時(shí)的鎖遷移問題
高級(jí)應(yīng)用場(chǎng)景
- 分布式系統(tǒng)中的分片策略
- 數(shù)據(jù)庫連接池的并發(fā)控制
- 多級(jí)緩存系統(tǒng)的同步機(jī)制
性能對(duì)比
| 鎖策略 | 并發(fā)度 | 內(nèi)存開銷 | 實(shí)現(xiàn)復(fù)雜度 |
|---|---|---|---|
| 全局鎖 | 低 | 小 | 簡(jiǎn)單 |
| 分段鎖 | 高 | 中等 | 中等 |
| 細(xì)粒度鎖 | 最高 | 大 | 復(fù)雜 |
4.2 避免鎖競(jìng)爭(zhēng):減少持有鎖的時(shí)間
優(yōu)化原則
- 臨界區(qū)最小化:只將真正需要同步的代碼放入同步塊
- 計(jì)算與同步分離:將復(fù)雜計(jì)算移到鎖外執(zhí)行
- 資源準(zhǔn)備前置:在獲取鎖前完成所有準(zhǔn)備工作
典型錯(cuò)誤模式
// 錯(cuò)誤示例:在鎖內(nèi)執(zhí)行IO操作
synchronized(lock) {
readFile(); // 耗時(shí)IO
processData();
writeFile(); // 耗時(shí)IO
}
最佳實(shí)踐
- 使用局部變量暫存計(jì)算結(jié)果
- 采用讀寫鎖分離讀寫操作
- 對(duì)長(zhǎng)時(shí)間操作實(shí)現(xiàn)可中斷鎖
性能影響
- 鎖持有時(shí)間減少50%,吞吐量可提升2-3倍
- 對(duì)于高頻交易系統(tǒng),這種優(yōu)化效果尤為明顯
4.3 利用鎖消除:避免不必要的加鎖
JVM優(yōu)化機(jī)制
- 逃逸分析:判斷對(duì)象是否可能被其他線程訪問
- 棧封閉:確認(rèn)對(duì)象生命周期僅限于當(dāng)前棧幀
- 同步消除:移除不必要的同步操作
適用條件
- 鎖對(duì)象是方法局部變量
- 鎖對(duì)象不會(huì)逃逸出當(dāng)前線程
- 同步塊內(nèi)沒有訪問共享資源
驗(yàn)證方法
# 禁用鎖消除進(jìn)行對(duì)比測(cè)試 java -XX:-EliminateLocks LockEliminationDemo
實(shí)際應(yīng)用
- 工具類方法中的臨時(shí)同步
- 不可變對(duì)象處理
- 線程封閉場(chǎng)景
4.4 合理使用鎖粗化:減少鎖的獲取與釋放次數(shù)
觸發(fā)條件
- 連續(xù)多次對(duì)同一鎖的獲取/釋放
- 循環(huán)體內(nèi)的同步操作
- 相鄰的同步代碼塊
實(shí)現(xiàn)方式
- JVM自動(dòng)優(yōu)化:HotSpot虛擬機(jī)會(huì)自動(dòng)檢測(cè)并優(yōu)化
- 手動(dòng)合并:開發(fā)人員顯式擴(kuò)大同步范圍
權(quán)衡考慮
| 因素 | 鎖細(xì)化 | 鎖粗化 |
|---|---|---|
| 并發(fā)度 | 高 | 低 |
| 鎖開銷 | 大 | 小 |
| 適用場(chǎng)景 | 競(jìng)爭(zhēng)激烈 | 操作密集 |
典型應(yīng)用
- 批量數(shù)據(jù)處理
- 事務(wù)性操作
- 流水線處理
優(yōu)化示例
// 優(yōu)化前:頻繁加鎖
for (Item item : items) {
synchronized(lock) {
process(item);
}
}
// 優(yōu)化后:?jiǎn)未渭渔i
synchronized(lock) {
for (Item item : items) {
process(item);
}
}注意:鎖粗化可能會(huì)降低并發(fā)度,需根據(jù)實(shí)際場(chǎng)景權(quán)衡使用。
五、synchronized 的常見問題與解決方案
5.1 死鎖:線程間相互等待鎖資源
問題描述
死鎖是指兩個(gè)或多個(gè)線程在執(zhí)行過程中,因爭(zhēng)奪鎖資源而相互等待的一種狀態(tài)。例如,線程 A 持有鎖 1,等待鎖 2;線程 B 持有鎖 2,等待鎖 1,此時(shí)兩個(gè)線程會(huì)一直相互等待,無法繼續(xù)執(zhí)行。
代碼示例(死鎖場(chǎng)景)
public class DeadLockDemo {
// 兩個(gè)不同的鎖對(duì)象
private final Object lock1 = new Object();
private final Object lock2 = new Object();
// 線程1執(zhí)行的方法:先獲取lock1,再獲取lock2
public void method1() {
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " 獲取lock1,等待lock2");
try {
Thread.sleep(100); // 模擬耗時(shí)操作,增加死鎖概率
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " 獲取lock2,執(zhí)行method1");
}
}
}
// 線程2執(zhí)行的方法:先獲取lock2,再獲取lock1
public void method2() {
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " 獲取lock2,等待lock1");
try {
Thread.sleep(100); // 模擬耗時(shí)操作,增加死鎖概率
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock1) {
System.out.println(Thread.currentThread().getName() + " 獲取lock1,執(zhí)行method2");
}
}
}
public static void main(String[] args) {
DeadLockDemo demo = new DeadLockDemo();
// 線程1執(zhí)行method1
new Thread(demo::method1, "線程1").start();
// 線程2執(zhí)行method2
new Thread(demo::method2, "線程2").start();
}
}執(zhí)行結(jié)果
線程 1 會(huì)輸出"獲取 lock1,等待 lock2",線程 2 會(huì)輸出"獲取 lock2,等待 lock1",之后兩個(gè)線程會(huì)一直阻塞,無法繼續(xù)執(zhí)行,形成死鎖。
死鎖產(chǎn)生的必要條件
- 互斥條件:鎖資源只能被一個(gè)線程持有。
- 請(qǐng)求與保持條件:線程持有一個(gè)鎖后,又請(qǐng)求其他鎖,且不釋放已持有的鎖。
- 不可剝奪條件:線程持有的鎖不能被其他線程強(qiáng)制剝奪,只能由線程自己釋放。
- 循環(huán)等待條件:多個(gè)線程形成環(huán)形等待鎖資源的關(guān)系(如線程 A 等線程 B 的鎖,線程 B 等線程 A 的鎖)。
解決方案
只要破壞死鎖產(chǎn)生的任意一個(gè)必要條件,即可避免死鎖。在實(shí)際開發(fā)中,常用的解決方案如下:
- 按固定順序獲取鎖:確保所有線程都按照相同的順序獲取多個(gè)鎖。例如,在上述示例中,讓method2也先獲取lock1,再獲取lock2,即可避免循環(huán)等待條件。
// 優(yōu)化后的method2:按固定順序獲取鎖(先lock1,再lock2)
public void method2() {
synchronized (lock1) { // 與method1的鎖獲取順序一致
System.out.println(Thread.currentThread().getName() + " 獲取lock1,等待lock2");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lock2) {
System.out.println(Thread.currentThread().getName() + " 獲取lock2,執(zhí)行method2");
}
}
}
- 使用tryLock()嘗試獲取鎖:使用
java.util.concurrent.locks.Lock接口的tryLock(long timeout, TimeUnit unit)方法,在指定時(shí)間內(nèi)嘗試獲取鎖。如果超時(shí)未獲取到鎖,則釋放已持有的鎖,避免線程永久阻塞。 - 定時(shí)釋放鎖:在持有鎖的線程中,設(shè)置超時(shí)機(jī)制,當(dāng)超過指定時(shí)間仍未獲取到其他鎖時(shí),主動(dòng)釋放已持有的鎖。
5.2 錯(cuò)誤的鎖對(duì)象:導(dǎo)致同步失效
問題描述
在使用synchronized修飾代碼塊時(shí),如果鎖對(duì)象選擇不當(dāng)(如使用可變對(duì)象、字符串常量池中的對(duì)象等),可能會(huì)導(dǎo)致同步失效,無法保證線程安全。
常見錯(cuò)誤場(chǎng)景1:使用可變對(duì)象作為鎖對(duì)象
public class BadLockObjectDemo1 {
// 可變鎖對(duì)象:value可能被修改
private String lock = "lock";
public void updateLockAndSync() {
// 先修改鎖對(duì)象的值(導(dǎo)致鎖對(duì)象引用改變)
lock = "newLock";
// 此時(shí)的鎖對(duì)象是"newLock",而非最初的"lock"
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " 執(zhí)行同步代碼");
}
}
public static void main(String[] args) {
BadLockObjectDemo1 demo = new BadLockObjectDemo1();
// 線程1和線程2可能使用不同的鎖對(duì)象,導(dǎo)致同步失效
new Thread(demo::updateLockAndSync, "線程1").start();
new Thread(demo::updateLockAndSync, "線程2").start();
}
}問題原因
鎖對(duì)象lock是一個(gè)可變的字符串引用,當(dāng)lock = "newLock"執(zhí)行后,鎖對(duì)象的引用發(fā)生了改變。此時(shí),線程 1 和線程 2 可能使用不同的鎖對(duì)象(線程 1 使用"lock",線程 2 使用"newLock"),導(dǎo)致同步代碼塊無法實(shí)現(xiàn)同步效果。
解決方案
使用不可變對(duì)象作為鎖對(duì)象,如private final Object lock = new Object();。final關(guān)鍵字確保鎖對(duì)象的引用不會(huì)被修改,從而保證同步的有效性。
常見錯(cuò)誤場(chǎng)景2:使用字符串常量池中的對(duì)象作為鎖對(duì)象
public class BadLockObjectDemo2 {
// 使用字符串常量作為鎖對(duì)象(存在于字符串常量池中)
private final String lock = "LOCK";
public void syncWithStringLock() {
synchronized (lock) {
System.out.println(Thread.currentThread().getName() + " 執(zhí)行同步代碼");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
BadLockObjectDemo2 demo1 = new BadLockObjectDemo2();
BadLockObjectDemo2 demo2 = new BadLockObjectDemo2();
// demo1和demo2的lock對(duì)象引用的是字符串常量池中的同一個(gè)"LOCK"對(duì)象
// 線程1和線程2會(huì)競(jìng)爭(zhēng)同一把鎖,導(dǎo)致本應(yīng)獨(dú)立的實(shí)例同步代碼塊相互阻塞
new Thread(() -> demo1.syncWithStringLock(), "線程1").start();
new Thread(() -> demo2.syncWithStringLock(), "線程2").start();
}
}問題原因
在Java中,字符串常量會(huì)被存儲(chǔ)在字符串常量池中,相同內(nèi)容的字符串常量會(huì)指向同一個(gè)對(duì)象。上述代碼中,demo1和demo2的lock變量都指向常量池中的同一個(gè)"LOCK"對(duì)象,導(dǎo)致兩個(gè)不同實(shí)例的同步代碼塊共享同一把鎖。當(dāng)線程1執(zhí)行demo1的syncWithStringLock方法時(shí),會(huì)持有該鎖,線程2執(zhí)行demo2的同名方法時(shí)會(huì)被阻塞,違背了"不同實(shí)例的同步代碼塊應(yīng)相互獨(dú)立"的預(yù)期。
解決方案
避免使用字符串常量或基本類型包裝類(如Integer,其緩存機(jī)制也會(huì)導(dǎo)致類似問題)作為鎖對(duì)象,改用new Object()創(chuàng)建獨(dú)立的鎖對(duì)象,或使用當(dāng)前實(shí)例(this)作為鎖對(duì)象(確保不同實(shí)例的鎖相互獨(dú)立)。
5.3 鎖競(jìng)爭(zhēng)導(dǎo)致的性能瓶頸
問題描述
在高并發(fā)場(chǎng)景下,如果多個(gè)線程頻繁競(jìng)爭(zhēng)同一把synchronized鎖,會(huì)導(dǎo)致大量線程阻塞和喚醒,引發(fā)用戶態(tài)與內(nèi)核態(tài)的切換,增加系統(tǒng)開銷,最終導(dǎo)致程序性能下降,出現(xiàn)響應(yīng)延遲、吞吐量降低等問題。
典型場(chǎng)景
在秒殺系統(tǒng)中,所有請(qǐng)求線程都需要競(jìng)爭(zhēng)同一把鎖來修改庫存變量,此時(shí)鎖競(jìng)爭(zhēng)會(huì)非常激烈,成為系統(tǒng)的性能瓶頸。
問題分析
當(dāng)鎖競(jìng)爭(zhēng)激烈時(shí),synchronized的鎖狀態(tài)會(huì)升級(jí)為重量級(jí)鎖,線程會(huì)進(jìn)入內(nèi)核態(tài)等待隊(duì)列。每次線程的阻塞和喚醒都需要切換內(nèi)核態(tài),而內(nèi)核態(tài)切換的開銷遠(yuǎn)大于用戶態(tài)操作,大量的內(nèi)核態(tài)切換會(huì)嚴(yán)重消耗CPU資源,導(dǎo)致程序處理請(qǐng)求的效率降低。
解決方案
- 減少鎖競(jìng)爭(zhēng)頻率:
- 例如,將秒殺庫存按商品ID分段,每個(gè)分段使用獨(dú)立的鎖,避免所有線程競(jìng)爭(zhēng)同一把鎖。
- 通過減小鎖粒度(如分段鎖)
- 使用無鎖數(shù)據(jù)結(jié)構(gòu)(如
AtomicInteger)
- 使用樂觀鎖替代悲觀鎖:
synchronized是悲觀鎖,默認(rèn)認(rèn)為線程會(huì)存在激烈競(jìng)爭(zhēng),每次獲取鎖都會(huì)阻塞其他線程。- 樂觀鎖(如基于
CAS的Atomic類、版本號(hào)機(jī)制)默認(rèn)認(rèn)為線程競(jìng)爭(zhēng)較少,通過重試機(jī)制實(shí)現(xiàn)同步,避免線程阻塞。
// 使用AtomicInteger(樂觀鎖)替代synchronized修改庫存
public class SeckillService {
// 無鎖的原子類,避免鎖競(jìng)爭(zhēng)
private AtomicInteger stock = new AtomicInteger(1000);
public boolean seckill() {
// CAS操作:如果當(dāng)前庫存大于0,就將庫存減1
int currentStock;
do {
currentStock = stock.get();
if (currentStock <= 0) {
return false;
}
} while (!stock.compareAndSet(currentStock, currentStock - 1));
return true;
}
}- 讀寫分離:
- 對(duì)于讀多寫少的場(chǎng)景,使用
ReadWriteLock或StampedLock - 允許多個(gè)線程同時(shí)讀取數(shù)據(jù),但寫操作需要獨(dú)占鎖
- 對(duì)于讀多寫少的場(chǎng)景,使用
- 鎖粗化與鎖消除:
- 鎖粗化:將多個(gè)連續(xù)的鎖操作合并為一個(gè)更大的鎖操作
- 鎖消除:JVM在編譯時(shí)進(jìn)行逃逸分析,消除不必要的鎖
- 使用并發(fā)容器:
- 如
ConcurrentHashMap、CopyOnWriteArrayList等 - 這些容器內(nèi)部已經(jīng)實(shí)現(xiàn)了高效的并發(fā)控制機(jī)制
- 如
六、synchronized 與 Lock 接口的對(duì)比
在 Java 并發(fā)編程中,除了synchronized關(guān)鍵字,java.util.concurrent.locks.Lock接口也是常用的同步手段。Lock接口提供了比synchronized更靈活的同步控制能力,但兩者在使用方式、功能特性和性能上存在差異。下面從多個(gè)維度對(duì)比兩者的區(qū)別,幫助開發(fā)者根據(jù)業(yè)務(wù)場(chǎng)景選擇合適的同步方式。
6.1 功能特性對(duì)比
synchronized 關(guān)鍵字
- 獲取鎖方式
- 隱式獲取:進(jìn)入同步代碼塊/方法時(shí)自動(dòng)獲取鎖,退出時(shí)自動(dòng)釋放鎖
- 示例:
synchronized(this) { ... }或public synchronized void method() { ... }
- 可中斷性
- 不可中斷:線程獲取鎖時(shí)會(huì)一直阻塞,無法被中斷
- 場(chǎng)景:在死鎖情況下無法通過中斷恢復(fù)
- 超時(shí)獲取鎖
- 不支持:線程會(huì)一直阻塞,直到獲取到鎖
- 風(fēng)險(xiǎn):可能導(dǎo)致線程永久阻塞
- 公平鎖支持
- 非公平鎖:默認(rèn)情況下,線程獲取鎖的順序不遵循請(qǐng)求順序
- JVM優(yōu)化:JDK 1.6后引入偏向鎖、輕量級(jí)鎖優(yōu)化
- 條件變量支持
- 單一條件變量:通過
wait()、notify()、notifyAll()實(shí)現(xiàn) - 限制:一個(gè)鎖對(duì)象只能對(duì)應(yīng)一個(gè)條件隊(duì)列
- 單一條件變量:通過
Lock 接口(以 ReentrantLock 為例)
- 獲取鎖方式
- 顯式獲?。和ㄟ^
lock()方法獲取鎖 - 必須通過
unlock()方法手動(dòng)釋放鎖(通常在finally塊中)
- 顯式獲?。和ㄟ^
- 示例:
lock.lock();
try {
// 臨界區(qū)代碼
} finally {
lock.unlock();
}
- 可中斷性
- 可中斷:支持
lockInterruptibly()方法 - 場(chǎng)景:可以響應(yīng)
Thread.interrupt()中斷等待
- 可中斷:支持
- 超時(shí)獲取鎖
- 支持:通過
tryLock(long timeout, TimeUnit unit)方法 - 優(yōu)勢(shì):避免線程永久阻塞,提高系統(tǒng)健壯性
- 支持:通過
- 公平鎖支持
- 可配置:通過構(gòu)造函數(shù)
ReentrantLock(boolean fair)配置 - 公平鎖(
fair=true):遵循FIFO原則 - 非公平鎖(
fair=false):默認(rèn)配置,性能更好
- 可配置:通過構(gòu)造函數(shù)
- 條件變量支持
- 多個(gè)條件變量:通過
newCondition()創(chuàng)建多個(gè)Condition對(duì)象 - 應(yīng)用:可以實(shí)現(xiàn)更復(fù)雜的線程間協(xié)調(diào)
- 示例:
- 多個(gè)條件變量:通過
Condition notEmpty = lock.newCondition(); Condition notFull = lock.newCondition();
6.2 代碼示例對(duì)比
6.2.1 synchronized 實(shí)現(xiàn)同步
public class SynchronizedExample {
private final Object lock = new Object(); // 鎖對(duì)象
private int count = 0; // 共享變量
// 隱式獲取和釋放鎖
public void increment() {
synchronized (lock) { // 同步代碼塊
count++; // 線程安全操作
}
}
public int getCount() {
synchronized (lock) { // 同步代碼塊
return count; // 線程安全讀取
}
}
// 同步方法示例
public synchronized void syncMethod() {
// 方法體自動(dòng)同步
}
}6.2.2 Lock 接口實(shí)現(xiàn)同步
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockExample {
private final Lock lock = new ReentrantLock(); // 顯式創(chuàng)建鎖對(duì)象
private int count = 0; // 共享變量
public void increment() {
// 顯式獲取鎖
lock.lock(); // 可能阻塞
try {
// 業(yè)務(wù)邏輯:修改共享變量
count++;
} finally {
// 必須在finally中釋放鎖,避免異常導(dǎo)致鎖泄漏
lock.unlock();
}
}
// 帶超時(shí)的嘗試獲取鎖
public boolean tryIncrement(long timeout, TimeUnit unit)
throws InterruptedException {
if (lock.tryLock(timeout, unit)) { // 嘗試獲取鎖
try {
count++;
return true;
} finally {
lock.unlock();
}
}
return false; // 獲取鎖失敗
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}6.3 適用場(chǎng)景選擇
優(yōu)先選擇 synchronized 的場(chǎng)景:
- 簡(jiǎn)單同步需求
- 單一線程安全的方法或代碼塊
- 不需要復(fù)雜的同步控制(如中斷、超時(shí))
- 代碼可維護(hù)性
- 希望代碼簡(jiǎn)潔易讀
- 減少手動(dòng)管理鎖的風(fēng)險(xiǎn)(如鎖泄漏)
- 性能考慮
- 低并發(fā)或中等并發(fā)場(chǎng)景
- JVM對(duì)synchronized的優(yōu)化(偏向鎖、輕量級(jí)鎖)能帶來較好性能
- 快速開發(fā)
- 原型開發(fā)或?qū)π阅芤蟛桓叩膱?chǎng)景
選擇 Lock 接口的場(chǎng)景:
- 高級(jí)同步需求
- 需要中斷正在等待鎖的線程
- 示例:處理超時(shí)任務(wù)時(shí)中斷等待
- 超時(shí)控制
- 需要設(shè)置超時(shí)時(shí)間,避免線程永久阻塞
- 應(yīng)用場(chǎng)景:網(wǎng)絡(luò)請(qǐng)求超時(shí)控制
- 公平性要求
- 需要公平鎖,確保線程按請(qǐng)求順序獲取鎖
- 應(yīng)用場(chǎng)景:對(duì)資源分配順序有嚴(yán)格要求的系統(tǒng)
- 復(fù)雜條件控制
- 需要多個(gè)條件變量實(shí)現(xiàn)精細(xì)控制
- 典型應(yīng)用:生產(chǎn)者-消費(fèi)者模型中分別喚醒生產(chǎn)者和消費(fèi)者
- 性能關(guān)鍵場(chǎng)景
- 高并發(fā)場(chǎng)景下需要更細(xì)粒度的控制
- 需要嘗試獲取鎖而不阻塞(tryLock)
- 鎖的可擴(kuò)展性
- 未來可能需要擴(kuò)展鎖的功能
- Lock接口提供了更多擴(kuò)展可能性
七、常用 JVM 參數(shù)與調(diào)試工具
7.1 與 synchronized 相關(guān)的 JVM 參數(shù)
詳細(xì)參數(shù)說明與應(yīng)用場(chǎng)景
| JVM 參數(shù) | 作用 | 默認(rèn)值 | 適用場(chǎng)景 | 注意事項(xiàng) |
|---|---|---|---|---|
| -XX:+UseBiasedLocking | 開啟偏向鎖優(yōu)化 | JDK 1.6~1.14 默認(rèn)開啟(JDK 15 后廢棄偏向鎖) | 適用于單線程重復(fù)訪問同步塊的場(chǎng)景 | JDK 15+ 已廢棄,在競(jìng)爭(zhēng)激烈場(chǎng)景下可能反而降低性能 |
| -XX:-UseBiasedLocking | 關(guān)閉偏向鎖優(yōu)化 | - | 多線程競(jìng)爭(zhēng)激烈場(chǎng)景 | 可減少鎖升級(jí)開銷,適用于高并發(fā)環(huán)境 |
| -XX:BiasedLockingStartupDelay | 偏向鎖啟動(dòng)延遲時(shí)間(毫秒) | 4000ms(JDK 1.6 及之后) | 需要延遲啟用偏向鎖的特殊場(chǎng)景 | 設(shè)置0表示立即啟用,常用于性能測(cè)試 |
| -XX:-EliminateLocks | 關(guān)閉鎖消除優(yōu)化 | 默認(rèn)開啟 | 調(diào)試鎖行為 | 會(huì)禁用JVM的逃逸分析優(yōu)化,影響性能 |
| -XX:-EliminateAllocations | 關(guān)閉標(biāo)量替換優(yōu)化(影響鎖消除) | 默認(rèn)開啟 | 調(diào)試逃逸分析相關(guān)行為 | 關(guān)閉后會(huì)影響鎖消除效果 |
| -XX:PreBlockSpin | 輕量級(jí)鎖自旋次數(shù) | JDK 1.6 后由 JVM 動(dòng)態(tài)調(diào)整,無需手動(dòng)設(shè)置 | 歷史版本調(diào)優(yōu) | 現(xiàn)代JVM已實(shí)現(xiàn)自適應(yīng)自旋,不建議手動(dòng)設(shè)置 |
典型應(yīng)用場(chǎng)景示例:
- 對(duì)于Web服務(wù)應(yīng)用,建議在JDK 15以下版本使用
-XX:+UseBiasedLocking以獲得更好的單線程性能 - 在高并發(fā)交易系統(tǒng)中,可以嘗試
-XX:-UseBiasedLocking來避免偏向鎖的撤銷開銷 - 調(diào)試死鎖問題時(shí),可以臨時(shí)關(guān)閉鎖消除
-XX:-EliminateLocks來觀察真實(shí)鎖競(jìng)爭(zhēng)情況
7.2 調(diào)試 synchronized 的工具
7.2.1 jstack 工具詳解
功能:用于打印Java進(jìn)程的線程堆棧信息,可查看線程的鎖持有情況和等待情況,幫助定位死鎖。
使用方式:
- 獲取Java進(jìn)程ID:
jps -l - 生成線程dump:
jstack -l <pid> > thread_dump.log - 分析dump文件中的鎖信息:
- 查找
BLOCKED狀態(tài)的線程 - 查看
waiting to lock <0x0000000712345678>信息 - 對(duì)應(yīng)查找
locked <0x0000000712345678>的線程
- 查找
示例輸出分析:
"Thread-1" #12 prio=5 os_prio=0 tid=0x00007f8a3418b000 nid=0x1a3e waiting for monitor entry [0x00007f8a2bdfe000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.example.DeadLock.run(DeadLock.java:20)
- waiting to lock <0x0000000712345678> (a java.lang.Object)
- locked <0x0000000712345690> (a java.lang.Object)"Thread-2" #13 prio=5 os_prio=0 tid=0x00007f8a3418d800 nid=0x1a3f waiting for monitor entry [0x00007f8a2bcfe000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.example.DeadLock.run(DeadLock.java:20)
- waiting to lock <0x0000000712345690> (a java.lang.Object)
- locked <0x0000000712345678> (a java.lang.Object)
7.2.2 jconsole 使用指南
功能特點(diǎn):
- 圖形化界面,直觀易用
- 實(shí)時(shí)監(jiān)控線程狀態(tài)
- 可視化展示鎖競(jìng)爭(zhēng)情況
操作步驟:
- 啟動(dòng)jconsole:
jconsole - 連接目標(biāo)Java進(jìn)程
- 切換到"線程"選項(xiàng)卡
- 點(diǎn)擊"同步監(jiān)視器"按鈕查看鎖信息
- 雙擊線程可查看詳細(xì)堆棧
適用場(chǎng)景:
- 開發(fā)環(huán)境實(shí)時(shí)監(jiān)控
- 演示和教學(xué)場(chǎng)景
- 快速驗(yàn)證鎖行為
7.2.3 VisualVM 高級(jí)功能
核心功能:
- 線程分析:
- 實(shí)時(shí)線程狀態(tài)監(jiān)控
- 線程dump生成與分析
- 死鎖自動(dòng)檢測(cè)
- 性能分析:
- CPU和內(nèi)存分析
- 方法級(jí)熱點(diǎn)分析
- 鎖競(jìng)爭(zhēng)統(tǒng)計(jì)
使用流程:
graph TD
A[啟動(dòng)VisualVM] --> B[連接目標(biāo)JVM]
B --> C[選擇"線程"標(biāo)簽]
C --> D[查看線程狀態(tài)圖]
D --> E[生成線程dump]
E --> F[分析鎖依賴關(guān)系]
插件擴(kuò)展:
- Threads Inspector:增強(qiáng)線程分析能力
- Deadlock Detector:自動(dòng)死鎖檢測(cè)
- Visual GC:可視化GC分析
7.2.4 Arthas 診斷實(shí)戰(zhàn)
- 常用命令詳解:
thread命令家族:thread:列出所有線程thread -b:檢測(cè)死鎖(找出阻塞線程及其鎖持有者)thread <id>:查看指定線程堆棧thread --state BLOCKED:過濾阻塞狀態(tài)線程
monitor命令:- 監(jiān)控方法級(jí)鎖競(jìng)爭(zhēng):
monitor -c 5 com.example.Service methodName
- 監(jiān)控方法級(jí)鎖競(jìng)爭(zhēng):
- 輸出字段說明:
timestamp:時(shí)間戳class:類名method:方法名total:調(diào)用次數(shù)success:成功次數(shù)fail:失敗次數(shù)avg-rt:平均響應(yīng)時(shí)間fail-rate:失敗率
- 高級(jí)診斷流程:
# 1. 查看阻塞線程
thread -b
# 2. 監(jiān)控可疑方法的鎖競(jìng)爭(zhēng)
monitor -c 5 com.example.Controller doPost
# 3. 觀察特定鎖對(duì)象的持有情況
watch java.lang.Object toString '{params[0], returnObj}' -x 2 -b -s com.example.LockHolder.monitor典型應(yīng)用場(chǎng)景:
- 生產(chǎn)環(huán)境快速診斷鎖問題
- 無法重現(xiàn)的偶發(fā)死鎖分析
- 性能瓶頸定位(鎖競(jìng)爭(zhēng))
到此這篇關(guān)于深入理解 Java 中的 synchronized 關(guān)鍵字的文章就介紹到這了,更多相關(guān)Java synchronized 關(guān)鍵字內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java并發(fā)編程必備之Synchronized關(guān)鍵字深入解析
- Java中復(fù)雜的Synchronized關(guān)鍵字使用方法詳解
- Java關(guān)鍵字synchronized基本使用詳解
- Java并發(fā)編程中的synchronized關(guān)鍵字詳細(xì)解讀
- Java的synchronized關(guān)鍵字深入解析
- Java?synchronized關(guān)鍵字性能考量及優(yōu)化探索
- 詳解Java synchronized關(guān)鍵字的用法
- 深入剖析Java中的synchronized關(guān)鍵字
- 深入了解Java中Synchronized關(guān)鍵字的實(shí)現(xiàn)原理
相關(guān)文章
Mybatis?sqlMapConfig.xml中的mappers標(biāo)簽使用
這篇文章主要介紹了Mybatis?sqlMapConfig.xml中的mappers標(biāo)簽使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。2022-01-01
使用Java操作MySQL實(shí)現(xiàn)數(shù)據(jù)交互的方法
JDBC是Java中用于操作數(shù)據(jù)庫的API,可以為多種關(guān)系數(shù)據(jù)庫提供統(tǒng)一訪問,它通過JDK自帶的JDBC API和數(shù)據(jù)庫驅(qū)動(dòng)包進(jìn)行操作,實(shí)現(xiàn)數(shù)據(jù)庫的增刪改查,本文給大家介紹使用Java操作MySQL實(shí)現(xiàn)數(shù)據(jù)交互的方法,感興趣的朋友跟隨小編一起看看吧2025-01-01
詳解spring cloud構(gòu)建微服務(wù)架構(gòu)的網(wǎng)關(guān)(API GateWay)
這篇文章主要介紹了詳解spring cloud構(gòu)建微服務(wù)架構(gòu)的網(wǎng)關(guān)(API GateWay),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-01-01
Java Web程序中利用Spring框架返回JSON格式的日期
這里我們來介紹一下Java Web程序中利用Spring框架返回JSON格式的日期的方法,前提注意使用@DatetimeFormat時(shí)要引入一個(gè)類庫joda-time-版本.jar,否則會(huì)無法訪問相應(yīng)路徑2016-05-05

