| 特性 | 定義 | 實現機制 |
| 可見性 | 一線程寫入,其他線程可見 | volatile、synchronized、final、JUC 類 |
| 有序性 | 程式順序 vs 執行順序 | volatile (禁止重排)、synchronized (鎖序)、happens-before |
| 原子性 | 操作不可分割 | synchronized、Lock、AtomicXXX、CAS |
happens-before 關鍵規則
- 程式順序規則:單線程內按代碼順序
- 鎖規則:unlock happens-before 後續 lock
- volatile 規則:寫 happens-before 後續讀
- 線程啟動規則:start() happens-before 線程內動作
- 線程終止規則:線程內動作 happens-before join() 返回
- 中斷規則:interrupt() happens-before 被中斷線程檢測到
- 傳遞性:A hb B, B hb C ⇒ A hb C
synchronized 深度解析
三種鎖定形式
// 1. 實例方法鎖:鎖當前對象 this
public synchronized void instanceMethod() { }
// 2. 靜態方法鎖:鎖 Class 對象
public static synchronized void staticMethod() { }
// 3. 同步代碼塊:鎖指定對象
public void blockSync() {
synchronized (this) { } // 鎖 this
synchronized (MyClass.class) { } // 鎖 Class
synchronized (sharedObject) { } // 鎖共享對象
}
鎖升級機制 (JDK 6+ 偏向鎖 → 輕量級鎖 → 重量級鎖)
| 鎖狀態 | 適用場景 | 實現 | 性能 |
| 無鎖 | 無併發 | 無 | 最快 |
| 偏向鎖 | 單線程重複獲取 | CAS 設置 ThreadID 到 Mark Word | 極快 (無同步開銷) |
| 輕量級鎖 | 多線程交替、低爭用 | CAS 將 Mark Word 指向棧鎖記錄 | 快 (自旋) |
| 重量級鎖 | 高爭用、長持有 | OS Mutex、線程阻塞/喚醒 | 慢 (上下文切換) |
經典錯誤:鎖對象不一致
// ❌ 錯誤:每次 new 新對象,鎖無效
public void wrong() {
synchronized (new Object()) { // 每次鎖不同對象!
criticalSection();
}
}
// ✅ 正確:共享不可變對象
private final Object lock = new Object();
public void correct() {
synchronized (lock) {
criticalSection();
}
}
// ✅ 正確:實例方法鎖 this (同一實例)
public synchronized void alsoCorrect() { }
wait/notify 機制與生產者消費者
class BoundedBuffer {
private final Object[] items = new Object[10];
int putIdx = 0, takeIdx = 0, count = 0;
public synchronized void put(Object item) throws InterruptedException {
while (count == items.length) wait(); // 滿則等待
items[putIdx] = item;
putIdx = (putIdx + 1) % items.length;
count++;
notifyAll(); // 喚醒消費者
}
public synchronized Object take() throws InterruptedException {
while (count == 0) wait(); // 空則等待
Object item = items[takeIdx];
items[takeIdx] = null;
takeIdx = (takeIdx + 1) % items.length;
count--;
notifyAll(); // 喚醒生產者
return item;
}
}
關鍵點:wait() 必須在 synchronized 內調用,釋放鎖並進入 WAITING;notifyAll() 唤醒所有等待線程,重新競爭鎖。
Lock 框架:ReentrantLock 進階特性
class ReentrantLockDemo {
private final ReentrantLock lock = new ReentrantLock(true); // 公平鎖
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
public void put(Object item) throws InterruptedException {
lock.lock();
try {
while (count == items.length) notFull.await(); // 精確喚醒
// ... 放入邏輯
notEmpty.signal();
} finally {
lock.unlock(); // 必須在 finally 釋放
}
}
}
優於 synchronized 特性
- 可中斷獲取:
lock.lockInterruptibly() 響應中斷
- 超時獲取:
lock.tryLock(timeout, unit) 避免死等
- 公平鎖選項:
new ReentrantLock(true) 先來先得
- 多條件變數:
newCondition() 精確喚醒 (生產者/消費者分離)
- 可查詢狀態:
isLocked(), getHoldCount(), getQueueLength()
volatile 關鍵字:輕量級同步
class VolatileDemo {
private volatile boolean running = true; // 可見性 + 禁止指令重排
private volatile int counter = 0; // 單次寫入原子,複合操作非原子
public void stop() { running = false; }
// ❌ 非原子:讀-改-寫三步驟
public void increment() { counter++; }
// ✅ 原子:用 AtomicInteger
private final AtomicInteger atomicCounter = new AtomicInteger(0);
public void atomicIncrement() { atomicCounter.incrementAndGet(); }
}
線程池與 Executor 體系 (JUC 核心)
// 生產級配置模板
ThreadPoolExecutor executor = new ThreadPoolExecutor(
corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(queueCapacity), // 有界隊列防 OOM
new NamedThreadFactory("biz-"), // 線程命名
new ThreadPoolExecutor.CallerRunsPolicy() // 拒絕策略:呼叫者執行
);
// 關閉流程
executor.shutdown(); // 不接受新任務,處理完隊列
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 強制關閉,返回未執行任務
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
並發容器選型指南
| 容器 | 適用場景 | 核心技術 |
| ConcurrentHashMap | 高併發 Map | JDK 8: CAS + Node + synchronized (bin 首節點) |
| CopyOnWriteArrayList | 讀多寫少、遍歷無鎖 | 寫時複製數組、快照迭代器 |
| BlockingQueue | 生產者消費者 | ReentrantLock + Condition (Array/Linked/Delay/Priority) |
| ConcurrentLinkedQueue | 無界非阻塞隊列 | CAS 無鎖鏈表 |
| LongAdder | 高併發計數器 | 分段累加 (Cell[]),sum() 時聚合 |
CompletableFuture 異步編程 (JDK 8+)
// 串聯
CompletableFuture.supplyAsync(() -> fetchUser(id))
.thenApply(user -> enrich(user))
.thenAccept(enriched -> cache.put(enriched))
.exceptionally(ex -> { log.error("失敗", ex); return null; });
// 並行聚合
CompletableFuture.allOf(
supplyAsync(() -> serviceA.call()),
supplyAsync(() -> serviceB.call()),
supplyAsync(() -> serviceC.call())
).join(); // 等待全部完成
// 異步回調扁平化
supplyAsync(() -> fetchUser(id))
.thenCompose(user -> supplyAsync(() -> fetchOrders(user.id)))
.thenAccept(orders -> render(orders));
死鎖與活鎖預防
- 固定鎖順序:全局規定獲取鎖的順序 (如按 hashCode 排序)
- 超時機制:
lock.tryLock(timeout) 避免無限等待
- 避免嵌套鎖:重構減少持有鎖調用外部方法
- 死鎖檢測:
jstack 分析、ThreadMXBean.findDeadlockedThreads()
面試高頻考點速查
| 問題 | 核心答案 |
| synchronized vs Lock | Lock 提供可中斷、超時、公平、多 Condition、可查狀態 |
| volatile 能否替代 synchronized | 不能:volatile 只保證可見性/有序性,不保證複合操作原子性 |
| 線程池參數如何設定 | CPU 密集:核心數+1;IO 密集:2*核心數;隊列有界;拒絕策略 CallerRuns |
| ConcurrentHashMap 1.7 vs 1.8 | 1.7 Segment 分段鎖;1.8 CAS + Node + synchronized (bin 首節點),更細粒度 |
| ThreadLocal 記憶體洩漏原因 | Key 為弱引用,Value 強引用,Thread 不銷毀導致 Value 洩漏 → 用完 remove() |
| 如何實現線程按序執行 | join()、CountDownLatch、CompletableFuture.thenCompose、單線程池 |
總結:並發編程成長路徑
- 基礎:掌握
synchronized/volatile/wait/notify/Thread 基礎 API
- 進階:理解 JMM、happens-before、鎖升級、線程池七參數、死鎖分析
- 實戰:熟練 JUC 容器、Lock、Condition、CompletableFuture、LongAdder、ThreadLocal
- 架構:無鎖設計、Actor 模型 (Akka)、CSP (Go Channel)、反應式流 (Reactor/RxJava)
- 診斷:Arthas、jstack、JMC、JFR、Async Profiler 定位併發瓶頸
- 上一篇文章: Java 呼叫預存程序獲取自增主鍵完整實戰:JDBC CallableStatement 與 GeneratedKeys 兩大方案
- 下一篇文章: Java HashMap 初始化與遍歷完全指南:從雙大括號語法到 JDK 9 集合工廠
Copyright ©
程式碼之家 All Rights Reserved