多線程併發訪問共享資源會導致資料競爭。本文系統整理同步代碼塊、同步方法、Lock 介面、死鎖成因與避免、釋放鎖時機、線程安全類選型原則,配合完整可執行範例。
synchronized(同步監視器對象) {
// 同步代碼:同一時間僅一線程執行
}
new Object()、this、ClassName.class
class TicketSeller implements Runnable {
private int tickets = 500;
private final Object lock = new Object(); // 專用鎖對象
@Override
public void run() {
synchronized (lock) {
for (; tickets > 0; tickets--) {
System.out.println(Thread.currentThread() + " 賣出第 " + tickets + " 張票");
try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
if (tickets == 1) System.out.println("票賣完,線程:" + Thread.currentThread());
}
}
}
}
public synchronized void run() { ... } // 實例方法,鎖為 this
public static synchronized void staticMethod() { ... } // 靜態方法,鎖為 ClassName.class
this當前類.class
class TicketSeller2 implements Runnable {
private int tickets = 500;
@Override
public synchronized void run() { // 鎖 this
for (; tickets > 0; tickets--) {
System.out.println(Thread.currentThread() + " 賣出第 " + tickets + " 張票");
}
}
}
| 情況 | 是否釋放鎖 |
|---|---|
| 同步代碼塊/方法正常結束 | ✓ 釋放 |
| break / return 結束 | ✓ 釋放 |
| 拋出 Exception / Error | ✓ 釋放 |
| 同步監視器呼叫 wait() | ✓ 釋放 (進入等待集) |
| Thread.sleep() / yield() | ✗ 不釋放 |
| 其他線程呼叫 suspend() | ✗ 不釋放 (不建議使用) |
lockInterruptibly()tryLock(timeout, unit)new ReentrantLock(true)newCondition() 支援多組等待隊列isLocked(), getHoldCount()
class TicketSeller3 implements Runnable {
private final ReentrantLock lock = new ReentrantLock();
private int tickets = 50;
@Override
public void run() {
lock.lock(); // 加鎖
try {
for (; tickets > 0; tickets--) {
System.out.println(Thread.currentThread() + " 賣出第 " + tickets + " 張票");
if (tickets == 1) System.out.println("票賣完:" + Thread.currentThread());
}
} finally {
lock.unlock(); // 必須在 finally 釋放
}
}
}
ReadLock:共享鎖,讀讀並行,讀寫互斥WriteLock:獨佔鎖,寫寫/寫讀互斥線程 A 持有鎖 1 等待鎖 2,線程 B 持有鎖 2 等待鎖 1 → 循環等待
class DeadLock implements Runnable {
Object a = new Object();
Object b = new Object();
public void init() {
new Thread(this).start(); // 子線程
aMethod(b); // 主線程
}
@Override
public void run() {
bMethod(a); // 子線程
}
public synchronized void aMethod(Object other) {
System.out.println(Thread.currentThread().getName() + " 執行 aMethod");
try { Thread.sleep(10); } catch (Exception e) {}
System.out.println("準備呼叫 other.bMethod()");
synchronized (other) { other.bMethod(); } // 需要 other 的鎖
}
public synchronized void bMethod() {
System.out.println("bMethod 執行");
}
}
tryLock(timeout) 超時放棄StringBuilder、多線程用 StringBuffer (JDK 模式)synchronized:隱式鎖、語法簡單、JVM 優化好、適用大多數場景Lock:顯式鎖、功能豐富 (可中斷、超時、公平、條件)、複雜場景首選