Java 線程同步的核心邏輯遵循:**加鎖 → 修改共享資源 → 釋放鎖**。本文通過三種主流方式實現多線程對共享變數 i 的安全遞增輸出,深度對比其原理與適用場景。
顯式指定鎖物件 (通常為共享資源本身),粒度精細,靈活度高。
public class SyncBlock {
static class DataWrap { int i; }
static class SyncBlockThread extends Thread {
private final DataWrap data;
public SyncBlockThread(DataWrap data) { this.data = data; }
@Override
public void run() {
for (int k = 0; k < 10; k++) {
synchronized (data) { // 鎖住共享資源 data
data.i++;
try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(getName() + " " + data.i);
}
}
}
}
public static void main(String[] args) {
DataWrap shared = new DataWrap();
new SyncBlockThread(shared).start();
new SyncBlockThread(shared).start();
new SyncBlockThread(shared).start();
}
}
關鍵點:鎖物件需為共享可見物件;sleep()/yield() 不會釋放鎖;同步塊外代碼並發執行,提高吞吐量。
隱式鎖定 this (實例方法) 或 Class 物件 (靜態方法)。簡潔但粒度較粗,調用該物件任何同步方法均需排隊。
public class SyncMethod {
static class DataWrap {
int i;
public synchronized void valueGrow() { // 鎖住 this (dataWrap 實例)
i++;
try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
// Thread 類與 main 方法結構同 SyncBlock,調用 dataWrap.valueGrow()
}
注意:一線程持有物件鎖時,其他線程調用該物件任意同步方法均阻塞,甚至包括無關業務邏輯的方法。
提供比 synchronized 更豐富的功能:可中斷獲取鎖、超時獲取、公平鎖/非公平鎖、條件變量 (Condition) 等。必須在 finally 中釋放鎖,防止死鎖。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class SyncLock {
static class DataWrap {
final Lock lock = new ReentrantLock(); // 可重入鎖
int i;
public void valueGrow() {
lock.lock(); // 加鎖
try {
i++;
try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + " " + i);
} finally {
lock.unlock(); // 關鍵:確保釋放鎖
}
}
}
// Thread 類與 main 方法結構同上
}
| 維度 | 同步代碼塊 | 同步方法 | Lock 鎖 |
|---|---|---|---|
| 鎖物件 | 任意物件 (顯式指定) | this / Class 物件 (隱式) | Lock 實例 (顯式管理) |
| 釋放機制 | 塊結束自動釋放 | 方法返回自動釋放 | 手動 unlock() (需 finally) |
| 靈活性 | 高 (可縮小同步範圍) | 低 (整個方法同步) | 最高 (可中斷、超時、公平、多條件) |
| 效能 (低競爭) | 優 | 優 | 略低 (API 開銷) |
| 效能 (高競爭) | 良 | 良 | 優 (ReentrantLock 優化較好) |
| 適用場景 | 精細控制同步範圍 | 簡單業務、整個方法需原子性 | 複雜同步邏輯、需高級特性 (讀寫鎖等) |
選擇原則:能用 synchronized 解決優先用 synchronized (JVM 優化成熟、代碼簡潔);需要高級特性 (超時、中斷、公平、多條件) 時引入 Lock。