線程同步核心邏輯
多線程環境下,對共享資源的訪問必須遵循 加鎖 → 修改 → 釋放鎖 的原子性操作序列。Java 提供了多層次的同步機制,從關鍵字到顯式鎖再到無鎖算法,適用於不同並發場景。
1. synchronized 關鍵字:JVM 內置鎖
同步代碼塊
public class SyncBlockDemo {
static class DataWrap { int counter = 0; }
static class Worker extends Thread {
private final DataWrap data;
Worker(DataWrap d) { this.data = d; }
@Override public void run() {
for (int i = 0; i < 10; i++) {
synchronized (data) { // 鎖住共享對象
data.counter++;
try { Thread.sleep(1); } catch (InterruptedException ignored) {}
System.out.println(getName() + " -> " + data.counter);
}
}
}
}
public static void main(String[] args) {
DataWrap shared = new DataWrap();
new Worker(shared).start();
new Worker(shared).start();
new Worker(shared).start();
}
}
- 鎖對象必須是共享可見的引用
sleep()、yield() 不釋放鎖
- 適用於簡單同步場景,代碼清晰
同步方法
public class SyncMethodDemo {
static class DataWrap {
int counter = 0;
public synchronized void increment() { // 鎖住 this
counter++;
try { Thread.sleep(1); } catch (InterruptedException ignored) {}
System.out.println(Thread.currentThread().getName() + " -> " + counter);
}
}
static class Worker extends Thread {
private final DataWrap data;
Worker(DataWrap d) { this.data = d; }
@Override public void run() {
for (int i = 0; i < 10; i++) data.increment();
}
}
public static void main(String[] args) {
DataWrap shared = new DataWrap();
new Worker(shared).start();
new Worker(shared).start();
new Worker(shared).start();
}
}
- 實例方法鎖
this,靜態方法鎖 Class 對象
- 同一對象的所有同步方法互斥,即使調用不同方法
- 避免在同步方法中調用耗時操作 (I/O、睡眠)
2. 顯式鎖 Lock 接口:更靈活的同步控制
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo {
static class DataWrap {
private final Lock lock = new ReentrantLock(); // 可重入鎖
int counter = 0;
public void increment() {
lock.lock(); // 手動加鎖
try {
counter++;
try { Thread.sleep(1); } catch (InterruptedException ignored) {}
System.out.println(Thread.currentThread().getName() + " -> " + counter);
} finally {
lock.unlock(); // 必須在 finally 中釋放
}
}
}
// Worker 與 main 類似...
}
Lock 核心優勢
| 特性 | synchronized | Lock |
| 可中斷獲取鎖 | ❌ | ✅ lockInterruptibly() |
| 嘗試非阻塞獲取 | ❌ | ✅ tryLock() |
| 超時獲取鎖 | ❌ | ✅ tryLock(timeout, unit) |
| 公平鎖選項 | ❌ (非公平) | ✅ new ReentrantLock(true) |
| 多條件變量 | ❌ (單一 wait/notify) | ✅ newCondition() |
| 死鎖檢測 | 困難 | 可通過 tryLock 實現避免 |
讀寫鎖 ReadWriteLock:讀多寫少場景優化
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class CacheDemo {
private final Map<String, Object> cache = new HashMap<>();
private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
public Object get(String key) {
rwLock.readLock().lock(); // 共享鎖,多線程可並發讀
try { return cache.get(key); }
finally { rwLock.readLock().unlock(); }
}
public void put(String key, Object value) {
rwLock.writeLock().lock(); // 排他鎖,寫操作獨佔
try { cache.put(key, value); }
finally { rwLock.writeLock().unlock(); }
}
}
3. 原子類:無鎖線程安全 (CAS)
import java.util.concurrent.atomic.*;
public class AtomicDemo {
// 原子整數
private final AtomicInteger counter = new AtomicInteger(0);
// 原子引用
private final AtomicReference<String> ref = new AtomicReference<>("init");
// 原子數組
private final AtomicIntegerArray array = new AtomicIntegerArray(10);
// 字段更新器 (非侵入式)
private static final AtomicIntegerFieldUpdater<Data> UPDATER =
AtomicIntegerFieldUpdater.newUpdater(Data.class, "value");
static class Data { volatile int value = 0; }
public void increment() {
// 原子自增,返回新值
int newVal = counter.incrementAndGet();
// 比較並設置 (CAS)
boolean updated = ref.compareAndSet("init", "updated");
// 數組原子操作
array.getAndIncrement(0);
// 字段原子更新
Data data = new Data();
UPDATER.incrementAndGet(data);
}
}
CAS 原理與 ABA 問題
- CAS:Compare-And-Swap,硬件級原子指令,
if (memory == expect) memory = update
- 自旋:失敗後重試,適用於低競爭、短耗時場景
- ABA 問題:值 A→B→A,CAS 認為未變。解決:
AtomicStampedReference (版本戳) 或 AtomicMarkableReference (標記位)
4. 線程安全集合:避免手動同步
| 非線程安全 | 同步包裝器 | 並發專用 (推薦) |
| ArrayList | Collections.synchronizedList | CopyOnWriteArrayList |
| HashMap | Collections.synchronizedMap | ConcurrentHashMap |
| HashSet | Collections.synchronizedSet | CopyOnWriteArraySet |
| Queue | - | ConcurrentLinkedQueue, ArrayBlockingQueue |
ConcurrentHashMap 核心機制 (JDK 8+)
- Node 數組 + 鏈表/紅黑樹
- CAS + synchronized 細粒度鎖 (僅鎖頭節點)
- 擴容支持多線程協助
transfer()
size() 使用 LongAdder 累加器
5. 高層同步工具類
// 計數器門閂:等待 N 個任務完成
CountDownLatch latch = new CountDownLatch(3);
executor.submit(() -> { doWork(); latch.countDown(); });
latch.await(); // 主線程阻塞直到歸零
// 循環柵欄:N 個線程相互等待
CyclicBarrier barrier = new CyclicBarrier(3, () -> System.out.println("全到齊"));
executor.submit(() -> { doWork(); barrier.await(); });
// 信號量:限制並發訪問數
Semaphore semaphore = new Semaphore(10); // 最多 10 個許可
semaphore.acquire();
try { doLimitedWork(); } finally { semaphore.release(); }
// 交換器:兩線程交換數據
Exchanger<Data> exchanger = new Exchanger<>();
executor.submit(() -> exchanger.exchange(produceData()));
executor.submit(() -> exchanger.exchange(consumeData()));
// Phaser: 可動態註冊的柵欄 (JDK 7+)
Phaser phaser = new Phaser(1); // 主線程註冊
for (int i = 0; i < 3; i++) {
phaser.register();
executor.submit(() -> { doPhase1(); phaser.arriveAndAwaitAdvance();
doPhase2(); phaser.arriveAndAwaitAdvance(); });
}
phaser.arriveAndDeregister();
6. 實戰選型指南
| 場景 | 推薦方案 | 理由 |
| 簡單計數器/標誌位 | AtomicInteger/AtomicBoolean | 無鎖、高性能、代碼簡潔 |
| 復雜業務邏輯互斥 | synchronized / ReentrantLock | 語義清晰、JVM 優化好、可重入 |
| 需要超時/中斷/公平性 | ReentrantLock | 顯式控制、診斷友好 |
| 讀多寫少緩存/配置 | ReadWriteLock / StampedLock | 讀並發、寫獨佔、StampedLock 更優 |
| 生產者消費者隊列 | BlockingQueue 實現類 | 內置阻塞、無需手動 wait/notify |
| 多階段任務協調 | Phaser / CyclicBarrier | 靈活的階段同步 |
| 高併發 Map/Set | ConcurrentHashMap / ConcurrentSkipListSet | 無鎖/細粒度鎖、擴展性強 |
7. 常見錯誤與避坑
- 鎖對象不共享:`new Object()` 每次不同、`String` 常量池、基本類型自動裝箱
- 釋放鎖漏寫:Lock 必須在 `finally` 中 `unlock()`
- 同步塊過大:包含 I/O、睡眠、複雜計算,降低並發度
- 死鎖:多鎖獲取順序不一致 → 統一全局獲取順序、使用 `tryLock` 超時退出
- 活鎖:過度禮讓重試 → 引入隨機退避
- volatile 誤用:只保證可見性,不保證複合操作原子性
掌握 Java 同步機制的層次與適用場景,是編寫高性能並發程序的基石。實際開發中優先使用高層並發工具 (JUC 包),減少底層鎖操作,並通過壓測驗證同步策略的有效性。
- 上一篇文章: Java 電商系統郵件發送功能完整實現指南
- 下一篇文章: Spring 框架整合 Quartz 定時任務完整配置教程
Copyright ©
程式碼之家 All Rights Reserved