第一部分:基礎概念與 Java 記憶體模型 (JMM)
進程與執行緒的本質區別
| 維度 | 進程 | 執行緒 |
| 定義 | 程式執行實例,獨立位址空間 | CPU 調度基本單位,共享進程資源 |
| 記憶體 | 獨立堆/棧,隔離性強 | 共享堆/方法區,私有程式計數器/棧 |
| 通訊 | IPC (管道、Socket、共享記憶體) | 共享變數、wait/notify、管道流 |
| 開銷 | 創建/切換成本高 | 輕量級,創建/切換快 |
JMM 解決的兩大核心問題
- 可見性:一個執行緒對共享變數的修改,何時對其他執行緒可見
- 有序性:指令重排序對多執行緒語義的影響
導致可見性問題的三大根源
// 1. CPU 時間片輪轉切換
// getNext() 位元碼指令序列 (非原子):
aload_0 // 載入 this
dup // 複製引用
getfield #12 // 讀取 value
dup_x1 // 複製值到頂部
iconst_1 // 推入常數 1
iadd // 整數相加
putfield #12 // 寫回 value
// 切換發生在任兩條指令間 → 競態條件
// 2. CPU 多級快取 (L1/L2/L3)
// 寫入先進快取,稍後刷回主存 → 時間窗口內不一致
// 3. 指令重排序 (編譯器/JIT/CPU)
// 單執行緒語義不變,多執行緒下可能觀測到異常順序
第二部分:同步機制層層遞進
Layer 1: volatile - 輕量級可見性保證
// 正確用法:狀態旗標
class Worker {
private volatile boolean done = false;
public void setDone(boolean done) { this.done = done; }
public void work() { while (!done) { /* 執行任務 */ } }
}
// 錯誤用法:複合操作非原子
class Counter {
public volatile static int count = 0;
public static void inc() { count++; } // 讀-改-寫三步,非原子!
}
volatile 語義:寫入後強制刷回主存;讀取前使快取失效重讀主存。
禁止指令重排越過 volatile 讀寫 (JSR-133 happens-before 規則)
Layer 2: synchronized - 互斥鎖與完整語義
// 三種鎖定範圍
public synchronized void method() { } // 實例鎖 (this)
public static synchronized void staticMethod() { } // 類鎖 (Class對象)
public void blockSync() {
synchronized(lockObject) { } // 任意物件鎖
}
synchronized 語義包含:互斥性 + 可見性 (鎖釋放前刷主存,獲取後失效快取) + 有序性 (禁止重排出同步塊)
Layer 3: wait/notify - 執行緒協作機制
// 生產者-消費者經典模式
class Buffer {
private Object item;
public synchronized void put(Object item) {
while (this.item != null) wait(); // 滿了等待
this.item = item;
notifyAll(); // 喚醒消費者
}
public synchronized Object take() {
while (item == null) wait(); // 空了等待
Object result = item;
item = null;
notifyAll(); // 喚醒生產者
return result;
}
}
關鍵點:wait() 釋放鎖進入等待集;notify() 隨機喚醒一個;notifyAll() 喚醒所有。
必須在 synchronized 內調用,否則拋出 IllegalMonitorStateException
Layer 4: Thread 狀態控制
// 睡眠:不釋放鎖!切勿在 synchronized 內 sleep
Thread.sleep(1000);
// 等待子執行緒結束
thread.join(); // 可帶超時
// 中斷請求 (協作式中斷)
thread.interrupt(); // 設置中斷標記
// 被中斷方處理:
try { Thread.sleep(1000); }
catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 重新設置標記
// 清理並退出
}
// yield():禮讓 CPU,不保證切換,測試用暴露併發bug
第三部分:非阻塞同步 - CAS 與 Atomic 包
CAS (Compare-And-Swap) 核心原理
// 硬體指令:原子性比較並交換
// 伪代碼:
boolean compareAndSet(int expect, int update) {
if (memoryValue == expect) {
memoryValue = update;
return true;
}
return false;
}
// 自旋重試模式
public final int getAndIncrement() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next)) return current;
}
}
Atomic 包三大類工具類
| 類別 | 代表類 | 適用場景 |
| 基本型別原子變數 | AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference | 計數器、旗標、單一物件引用 |
| 陣列原子操作 | AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray | 共享陣列元素原子更新 |
| 欄位更新器 (反射) | AtomicIntegerFieldUpdater, AtomicReferenceFieldUpdater | 第三方類別無法修改時,針對 volatile 欄位 |
實戰:無鎖計數器
class AtomicIdGenerator {
private final AtomicInteger counter = new AtomicInteger(0);
public int getNext() { return counter.getAndIncrement(); }
}
第四部分:高階同步工具 - java.util.concurrent
Lock 接口與 ReentrantLock
Lock lock = new ReentrantLock(true); // 公平鎖
lock.lock();
try {
// 臨界區
} finally {
lock.unlock(); // 必須在 finally 釋放
}
// 進階功能
lock.lockInterruptibly(); // 可中斷獲取鎖
lock.tryLock(); // 非阻塞嘗試
lock.tryLock(5, TimeUnit.SECONDS); // 帶超時嘗試
ReadWriteLock - 讀寫分離
ReadWriteLock rwLock = new ReentrantReadWriteLock();
Lock readLock = rwLock.readLock(); // 共享鎖,多讀並行
Lock writeLock = rwLock.writeLock(); // 排他鎖,寫寫/讀寫互斥
// 適用:讀多寫少場景 (配置、快取)
readLock.lock();
try { return cache.get(key); }
finally { readLock.unlock(); }
Condition - 更靈活的等待/通知
Lock lock = new ReentrantLock();
Condition notEmpty = lock.newCondition();
Condition notFull = lock.newCondition();
// 生產者
lock.lock();
try {
while (queue.size() == capacity) notFull.await();
queue.add(item);
notEmpty.signal();
} finally { lock.unlock(); }
核心同步器 AQS (AbstractQueuedSynchronizer)
AQS 是 ReentrantLock、Semaphore、CountDownLatch 等同步器的基礎框架。
核心:state (int/long) 表示共享資源狀態 + FIFO 等待佇列 + CAS 更新 state。
兩種模式:獨佔模式 (tryAcquire/tryRelease) + 共享模式 (tryAcquireShared/tryReleaseShared)
第五部分:高階同步物件 - 開箱即用
Semaphore - 信號量 (資源池控制)
Semaphore semaphore = new Semaphore(2, true); // 2個許可證,公平模式
// 獲取資源
semaphore.acquire(); // 阻塞
semaphore.tryAcquire(); // 非阻塞
// 釋放資源
semaphore.release();
CountDownLatch - 倒數閂 (一次性等待)
CountDownLatch latch = new CountDownLatch(3); // 等待3個任務
// 工作執行緒
new Thread(() -> { doWork(); latch.countDown(); }).start();
// 主執行緒等待
latch.await(); // 阻塞直到計數歸零
CyclicBarrier - 循環柵欄 (可重用、可攜帶動作)
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("三方均就緒,執行彙總邏輯");
});
// 每個參與者
barrier.await(); // 等待其他方
Exchanger - 資料交換器 (雙方換手)
Exchanger<StringBuilder> exchanger = new Exchanger<>();
// 線程A
StringBuilder data = exchanger.exchange(new StringBuilder("A的資料"));
// 線程B
StringBuilder data = exchanger.exchange(new StringBuilder("B的資料"));
第六部分:併發集合 - 高效能資料結構
| 介面/類別 | 特性 | 適用場景 |
| ConcurrentHashMap | 分段鎖/ CAS,高併發讀寫,弱一致性迭代器 | 共享快取、計數器、註冊表 |
| CopyOnWriteArrayList | 寫時複製,讀無鎖,迭代器快照隔離 | 事件監聽器列表、配置白名單 (讀多寫極少) |
| BlockingQueue | 阻塞式佇列,生產者-消費者天然支援 | 任務佇列、訊息總線、線程池工作佇列 |
| ConcurrentLinkedQueue | 非阻塞無界佇列,CAS 實現 | 高吞吐非阻塞傳遞 |
第七部分:執行緒池與任務執行框架
核心介面體系
Executor → ExecutorService → ScheduledExecutorService
↓ ↓ ↓
execute() submit()/invokeAll() scheduleAtFixedRate()
實戰:優雅關閉執行緒池
ExecutorService executor = Executors.newFixedThreadPool(10);
public void shutdownGracefully() {
executor.shutdown(); // 不再接受新任務
try {
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 強制中斷執行中任務
executor.awaitTermination(60, TimeUnit.SECONDS);
}
} catch (InterruptedException ex) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
CompletionService - 異步結果收集
ExecutorService executor = Executors.newFixedThreadPool(5);
CompletionService<Result> completion = new ExecutorCompletionService<>(executor);
for (Task task : tasks) {
completion.submit(task);
}
for (int i = 0; i < tasks.size(); i++) {
Future<Result> future = completion.take(); // 阻塞取得最快完成者
process(future.get());
}
第八部分:Java 7+ 新特性 - Fork/Join 與 Phaser
Fork/Join 框架 - 分治演算法並行化
// 任務定義
class MaxTask extends RecursiveTask<Long> {
private final long[] array;
private final int lo, hi;
private static final int THRESHOLD = 10000;
@Override
protected Long compute() {
if (hi - lo < THRESHOLD) {
return sequentialMax(); // 基礎情況:循序計算
}
int mid = (lo + hi) >>> 1;
MaxTask left = new MaxTask(array, lo, mid);
MaxTask right = new MaxTask(array, mid, hi);
left.fork(); // 非同步提交子任務
return Math.max(right.compute(), left.join()); // 合併結果
}
}
// 執行
ForkJoinPool pool = new ForkJoinPool();
Long max = pool.invoke(new MaxTask(array, 0, array.length));
核心優勢:工作竊取演算法 - 閒置執行緒主動竊取其他執行緒佇列任務,減少等待,最大化 CPU 利用率
Phaser - 多階段同步器
Phaser phaser = new Phaser(1); // 主線程註冊
// 階段1: 解析 HTML
phaser.register(); // 子任務註冊
new Thread(() -> { parseHtml(); phaser.arriveAndAwaitAdvance(); }).start();
// 階段2: 下載圖片
phaser.arriveAndAwaitAdvance(); // 主線程等待階段1完成
// ... 子任務下載 ...
// 階段3: 完成
phaser.arriveAndDeregister(); // 所有任務退出
第九部分:ThreadLocal - 執行緒封閉變數
// 每個執行緒獨立的 ID 生成器
class ThreadLocalIdGenerator {
private static final ThreadLocal<IdGenerator> local =
ThreadLocal.withInitial(IdGenerator::new);
public static int getNext() { return local.get().getNext(); }
}
// 實際應用:資料庫連線持有者、Spring 事務管理、Request 上下文
總結:技術選型決策樹
需要同步嗎?
├── 否 → 無需處理 (不可變物件、執行緒封閉)
└── 是 → 是否為簡單狀態旗標?
├── 是 → volatile
└── 否 → 是否為計數器/單值更新?
├── 是 → AtomicInteger/Long/Reference
└── 否 → 是否為讀多寫少?
├── 是 → ReadWriteLock / ConcurrentHashMap / CopyOnWriteArrayList
└── 否 → 是否為生產者-消費者?
├── 是 → BlockingQueue / Semaphore
└── 否 → 是否需複雜條件等待?
├── 是 → Lock + Condition / Phaser
└── 否 → synchronized (最後手段,簡單可靠)
現代 Java 併發開發應優先使用 java.util.concurrent 高階工具,僅在框架無法滿足時降級至 Atomic/Locks,最後才考慮 synchronized/volatile 等低階原語。理解 JMM 與 happens-before 規則是正確推理併發程式行為的基石。