? 1. 什么是線程安全?
線程安全指的是:當多個線程同時訪問同一塊代碼時,無論運行時環境采用怎樣的調度方式或者這些線程將怎樣交替執行,代碼的行為都能正確執行,且不會出現數據不一致、臟數據或異常崩潰。
舉個簡單例子:
// 非線程安全示例
private int count = 0;public void increment() {count++;
}
多線程同時調用 increment()
,因為 count++
不是原子操作,可能導致最終結果不正確。
🔥 2. 保證線程安全的幾種常見方式
方式 | 核心思路 | 適用場景 |
---|---|---|
使用同步機制(synchronized) | 控制同一時刻只能有一個線程訪問關鍵代碼區 | 輕量級同步,競爭不激烈時 |
使用顯式鎖(ReentrantLock) | 手動加鎖和釋放,支持更靈活的鎖粒度控制 | 有鎖超時、可中斷、讀寫鎖需求時 |
使用原子類(AtomicInteger 等) | 利用底層 CAS 實現無鎖線程安全操作 | 簡單計數器、自增器 |
使用線程安全容器(如 ConcurrentHashMap) | 內部已經實現了并發控制 | 需要高并發訪問集合時 |
局部變量 | 每個線程有自己獨立的數據,無共享風險 | 臨時計算或業務無狀態場景 |
ThreadLocal 機制 | 為每個線程提供獨立變量副本,避免共享沖突 | 保存用戶信息、請求上下文 |
無狀態設計(Stateless) | 類或方法不維護任何可變狀態,不存在數據競爭問題 | 純邏輯計算、工具類 |
🎯 3. 示例理解
3.1 使用 synchronized
public synchronized void increment() {count++;
}
或者鎖住特定代碼塊:
public void increment() {synchronized(this) {count++;}
}
🔵 注意:synchronized屬于悲觀鎖,性能有一定損耗,適合簡單場景。
3.2 使用 ReentrantLock
private final ReentrantLock lock = new ReentrantLock();public void increment() {lock.lock();try {count++;} finally {lock.unlock();}
}
🔵 支持更豐富的功能,比如可中斷鎖、嘗試加鎖、可重入。
3.3 使用原子類 AtomicInteger
private final AtomicInteger count = new AtomicInteger(0);public void increment() {count.incrementAndGet();
}
🔵 基于 CAS (Compare And Swap) 算法實現,無鎖,適合高并發下的簡單操作。
3.4 使用線程安全容器
private final Map<String, String> map = new ConcurrentHashMap<>();
🔵 適合高并發讀寫,比如緩存、用戶會話存儲。
3.5 使用 ThreadLocal
private ThreadLocal<Integer> threadLocalCount = ThreadLocal.withInitial(() -> 0);public void increment() {threadLocalCount.set(threadLocalCount.get() + 1);
}
🔵 每個線程有自己獨立的 count
,互不干擾,非常適合每個線程獨立上下文數據。
📊 總結一下
用一張 Mermaid 流程圖來快速理解選用策略:
🧠 面試常見延伸問題
- synchronized 和 Lock 的區別?
- CAS 的原理?CAS 會有什么問題?如何解決?
- 為什么推薦盡可能使用無鎖編程(Lock-Free Programming)?