隨著多核 CPU 普及,高效利用並行計算能力成為大規模應用的關鍵。Java java.util.concurrent (JUC) 包提供了工業級的並發工具集,本文將從源碼層面深度剖析 atomic (原子類)、locks (鎖機制)、concurrent (並發集合/線程池) 三大核心模塊,揭示其底層實現原理與設計智慧。

atomic 包提供 12 個原子類,涵蓋四類更新方式:基本類型 (AtomicInteger/Long/Boolean)、數組 (AtomicIntegerArray/LongArray/ReferenceArray)、引用 (AtomicReference/StampedReference/MarkableReference)、字段更新器 (AtomicIntegerFieldUpdater 等)。其核心均基於 CAS (Compare-And-Swap) 指令實現樂觀鎖語義:
所有原子類底層委派 sun.misc.Unsafe 的原生方法:
// AtomicInteger.incrementAndGet() 源碼鏈路
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
// Unsafe.getAndAddInt 實現 (JVM 內部)
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
v = getIntVolatile(o, offset); // volatile 讀取保證可見性
} while (!compareAndSwapInt(o, offset, v, v + delta)); // CAS 自旋
return v;
}
JDK 5+ 將 CAS 映射為 CPU 級指令 (x86: LOCK CMPXCHG,ARM: LDREX/STREX),由硬體保證原子性。JVM 會根據平台選擇最高效實現,甚至在無 CAS 支持的架構上退化為內部鎖。
public class AtomicDemo {
public static void main(String[] args) throws InterruptedException {
ExecutorService executor = Executors.newCachedThreadPool();
AtomicInteger counter = new AtomicInteger(0);
AtomicIntegerArray arr = new AtomicIntegerArray(10);
AtomicReference ref = new AtomicReference<>(new User("init", 0));
// 並發自增 10 萬次
for (int i = 0; i < 10; i++) {
executor.execute(() -> {
for (int j = 0; j < 10000; j++) counter.incrementAndGet();
});
executor.execute(() -> {
for (int j = 0; j < 10; j++)
for (int k = 0; k < 10; k++) arr.getAndAdd(k, 1);
});
executor.execute(() -> ref.set(new User("luoxn28", 23)));
}
executor.shutdown();
Thread.sleep(100);
System.out.println("Counter: " + counter); // 精確 100000
System.out.println("Array[0]: " + arr.get(0)); // 精確 100
System.out.println("Ref: " + ref.get());
}
}
public interface Lock {
void lock(); // 阻塞獲取鎖
void lockInterruptibly() throws InterruptedException; // 可中斷獲取
boolean tryLock(); // 非阻塞嘗試
boolean tryLock(long time, TimeUnit unit) throws InterruptedException; // 超時獲取
void unlock(); // 釋放鎖
Condition newCondition(); // 關聯條件變量
}
fair),非公平預設吞吐更高lock() 需同次數 unlock()Condition 替代 Object.wait/notify,支援多條件變量、可中斷等待、超時等待public class LockDemo {
public static void main(String[] args) throws InterruptedException {
Lock lock = new ReentrantLock();
AtomicInteger data = new AtomicInteger(0);
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 2; i++) {
executor.execute(() -> {
for (int j = 0; j < 10; j++) {
lock.lockInterruptibly();
try { data.getAndIncrement(); }
catch (InterruptedException e) { e.printStackTrace(); }
finally { lock.unlock(); }
}
});
}
executor.shutdown();
Thread.sleep(10);
System.out.println(data.get()); // 20
}
}
核心方法語義:
put(e):隊列滿阻塞等待take():隊列空阻塞等待offer(e, timeout)/poll(timeout):超時控制ReentrantLock + 兩個 Condition (notEmpty/notFull)putIndex/takeIndex 環形游標,count 計數器putLock/takeLock 分離,生產消費並行notFull/notEmpty Conditioncount 用 AtomicInteger 避免雙鎖競爭enqueue 尾插,出隊 dequeue 頭取並斷鏈助 GC
val/next 為 volatile,禁止 setValue 直接修改next 指針Node<K,V>[] 數組,長度為 2 的冪次final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable(); // 懶初始化
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// 空槽位:CAS 無鎖插入
if (casTabAt(tab, i, null, new Node<>(hash, key, value, null)))
break;
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f); // 協助擴容
else {
V oldVal = null;
synchronized (f) { // 細粒度鎖:鎖住鏈表/樹頭節點
if (tabAt(tab, i) == f) {
if (fh >= 0) { // 鏈表模式
binCount = 1;
for (Node<K,V> e = f;; ++binCount) {
if (e.hash == hash && keyEquals(e.key, key)) {
oldVal = e.val; if (!onlyIfAbsent) e.val = value; break;
}
if (e.next == null) { e.next = new Node<>(hash, key, value, null); break; }
e = e.next;
}
}
else if (f instanceof TreeBin) { // 紅黑樹模式
binCount = 2;
TreeNode<K,V> p = ((TreeBin<K,V>)f).putTreeVal(hash, key, value);
if (p != null) { oldVal = p.val; if (!onlyIfAbsent) p.val = value; }
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD) treeifyBin(tab, i); // 鏈表轉樹
if (oldVal != null) return oldVal;
break;
}
}
}
addCount(1L, binCount); // 更新 sizeCtl
return null;
}
MOVED 節點標記 + 多線程協助擴容 (helpTransfer)volatile 讀保證可見性,鏈表/樹遍歷無需同步ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
ExecutorService es = Executors.newCachedThreadPool();
for (int i = 0; i < 3; i++) es.execute(() -> {
for (int j = 0; j < 10; j++) map.put(UUID.randomUUID().toString(), UUID.randomUUID().toString());
});
es.shutdown(); Thread.sleep(2000);
map.forEach((k, v) -> System.out.println(k + " -> " + v));
| 場景 | 推薦方案 | 核心原理 |
|---|---|---|
| 計數器、序列號、開關狀態 | AtomicLong/Integer/Boolean | CAS 自旋無鎖 |
| 複雜同步邏輯、需條件變量、可中斷鎖 | ReentrantLock + Condition | AQS 隊列同步器 |
| 高併發緩存、計數統計、去重集合 | ConcurrentHashMap | CAS + synchronized 細粒度鎖 |
| 生產消費隊列、任務調度、背壓控制 | ArrayBlockingQueue / LinkedBlockingQueue | 雙鎖/單鎖 + Condition |
| 異步任務執行、定時任務、線程隔離 | ThreadPoolExecutor / ScheduledThreadPoolExecutor | 工作隊列 + 工作線程池 |
深入理解 JUC 源碼不僅能指導選型調優,更能培養無鎖編程、隊列同步、狀態機設計等核心並發思維。建議結合 AQS、ForkJoinPool、CompletableFuture 等進階組件持續深入探索。
參考資料:
1. ConcurrentHashMap 源碼分析 (JDK 8 版本)
2. Java 並發基礎總結