編寫線程安全代碼需關注兩個核心條件:
只有同時滿足「共享」且「可變」時,才會產生線程安全問題。
public class UnsafeCounter {
private int count = 0; // 共享且可變
public void increase() { // 無同步機制
count++; // 非原子操作:讀取-修改-寫入
}
public int getCount() {
return count;
}
}
count++ 編譯為三條位元組碼指令:getfield、iadd、putfield,多線程交執行時會導致更新丟失。
將變量限制在單線程內部,天然線程安全。
// 方法區域變量:每次調用創建新副本,線程隔離
public int add(int count) {
return ++count; // 區域變量在棧上,線程私有
}
// ThreadLocal:每線程獨立副本
private final ThreadLocal<Integer> threadLocalCount =
ThreadLocal.withInitial(() -> 0);
public void increase() {
threadLocalCount.set(threadLocalCount.get() + 1);
}
適用場景:請求級數據、資料庫連線、SimpleDateFormat 等非線程安全物件。
// final 修飾基本類型:引用不可變,值也不可變
private final int COUNT = 0;
// final 修飾引用類型:引用不可變,但對象內部狀態可能可變
private final Map<String, String> CONFIG = Collections.unmodifiableMap(new HashMap<>());
// 真正不可變對象:所有欄位 final、無 setter、不洩露 this 參考
public final class ImmutablePoint {
private final int x, y;
public ImmutablePoint(int x, int y) { this.x = x; this.y = y; }
public int getX() { return x; }
public int getY() { return y; }
}
核心原則:不可變對象永遠線程安全,無需同步。
// 方法級同步:鎖定 this
public synchronized void increase() {
count++;
}
// 代碼塊同步:縮小鎖範圍
public void increase() {
synchronized(this) {
count++;
}
}
// 顯式 Lock:功能更豐富
private final ReentrantLock lock = new ReentrantLock();
public void increase() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicCounter {
private final AtomicInteger count = new AtomicInteger(0);
public void increase() {
count.incrementAndGet(); // 原子性自增
}
public int getCount() {
return count.get();
}
// 複合操作仍需注意:compareAndSet 支援 CAS
public boolean compareAndSet(int expect, int update) {
return count.compareAndSet(expect, update);
}
}
| 場景 | 推薦方案 | 理由 |
|---|---|---|
| 簡單計數/旗標 | AtomicInteger/AtomicBoolean | 無鎖、效能佳、API 簡潔 |
| 複雜不變式/多變量協同 | synchronized / ReentrantLock | 支援複雜邏輯、條件變量 |
| 請求級臨時數據 | 線程封閉 / ThreadLocal | 零同步開銷、天然隔離 |
| 配置/常量/DTO | 不可變模式 | 零同步、安全發布、易推理 |
volatile 僅保證可見性,**不保證原子性**(如 i++ 仍不安全)synchronized 方法鎖的是 this,靜態方法鎖的是 Class 對象Vector、Hashtable 雖同步但效能差,優先用並發集合