多線程是 Java 面試必考陣地,覆蓋基礎 API、內存模型、鎖機制、併發工具、線程池、經典模式、故障診斷等全維度。本文精選 50 道高頻題,按知識體系分類,提供核心考點解析、標準回答模板、源碼級深度延伸與避坑提示,助你構建完整知識圖譜,從容應對初中高級崗位面試。
標準答: 線程是 CPU 調度基本單位,進程是資源分配基本單位。一進程含多線程,共享堆/方法區,私有棧/程序計數器。線程切換開銷小於進程。
深度點: Linux 下線程實現為輕量級進程 (LWP),共享 task_struct 部分字段;Java 線程 1:1 映射內核線程。
Thread 重寫 run()Runnable + new Thread(runnable)Callable + FutureTask (有返回值、可拋異常)ExecutorService.submit() (生產首選)選型: 優先線程池 → Runnable (無返回) / Callable (有返回)。
start() vs run() 區別?start() 啟動新線程,JVM 調用 run();直接調用 run() 為主線程同步執行,無新線程。
Runnable vs Callable?| Runnable | Callable | |
|---|---|---|
| 返回值 | 無 | 泛型 V |
| 異常 | 不拋檢查異常 | 可拋異常 |
| 配合 | Thread/Executor | FutureTask/ExecutorService.submit |
NEW → RUNNABLE (就緒/運行) → BLOCKED (同步鎖) / WAITING (wait/join) / TIMED_WAITING (sleep/waitN) → TERMINATED。注意: Java 無 Running 狀態,RUNNABLE 含就緒與運行。
sleep() vs wait() 區別?| sleep | wait | |
|---|---|---|
| 所屬類 | Thread | Object |
| 釋放鎖 | 否 | 是 |
| 喚醒方式 | 時間到 | notify/notifyAll/interrupt |
| 調用前提 | 無 | 必須持有對象鎖 (同步塊) |
yield() 作用?提示調度器當前線程願意讓出 CPU,同優先級線程可能獲取執行權。**不保證** 讓出成功,不釋放鎖。
為用戶線程服務 (GC、JIT 編譯、Netty Reactor)。JVM 只有守護線程時退出。setDaemon(true) 必須在 start() 前調用。
synchronized 實現原理?基於 Monitor 對象,編譯為 monitorenter/monitorexit 字節碼。對象頭 Mark Word 存儲鎖狀態、線程 ID、遞歸計數。JDK 6+ 引入偏向鎖→輕量級鎖(自旋+CAS)→重量級鎖(OS Mutex)膨脹機制。
volatile 關鍵字語義?① 可見性:寫入後立即刷新主存,讀取前失效緩存重讀主存。② 禁止指令重排 (內存屏障)。**不保證原子性** (如 i++ 仍非原子)。
synchronized vs ReentrantLock?| 維度 | synchronized | ReentrantLock |
|---|---|---|
| 鎖釋放 | 自動 (塊結束/異常) | 手動 unlock() (finally) |
| 可中斷 | 否 | lockInterruptibly() |
| 公平性 | 非公平 | 可選公平/非公平 |
| 條件變量 | wait/notify | 多 Condition |
| 性能 | JDK 8+ 優化極佳 | 高併發下略優 |
基於 state 狀態 + CLH 隊列 + CAS + LockSupport.park/unpark。獨佔/共享模式。tryAcquire/tryRelease 由子類實現語義。核心方法:acquire/release/acquireShared/releaseShared。
Mark Word 鎖標誌位演進:01 無鎖/偏向 → 00 輕量級 (CAS+自旋) → 10 重量級 (OS 互斥量)。偏向鎖針對無競爭優化,輕量級鎖針對低競爭自旋,重量級鎖阻塞等待。
ThreadLocal 原理與內存洩漏?每個 Thread 持有 ThreadLocalMap,Key 為 WeakReference,Value 為對象。**洩漏源:Key 弱引用 GC 後,Value 強引用鏈未斷 (Thread 存活)**。解決:remove()、靜態 ThreadLocal、線程池使用後清理。
CAS (CompareAndSwap) 原理與 ABA 問題?CPU 指令 cmpxchg,原子比較並交換。ABA:值 A→B→A,CAS 認為未變。解決:AtomicStampedReference (版本戳)、AtomicMarkableReference (布爾標記)。
CountDownLatch vs CyclicBarrier vs Semaphore?| CountDownLatch | CyclicBarrier | Semaphore | |
|---|---|---|---|
| 核心語義 | 倒計時門閂 | 循環屏障 | 信號量/許可證 |
| 可重用 | 否 | 是 | 是 |
| 典型場景 | 主線程等待多子線程完成 | 多線程分階段同步 | 資源池控制/限流 |
互斥、請求保持、不剝奪、循環等待。破壞任一即可。策略:① 統一加鎖順序 (有序資源編碼) ② tryLock(timeout) 超時放棄 ③ 避免長事務持有鎖 ④ 鎖分段/細粒度。
活鎖:線程狀態持續變化但無法推進 (如雙向禮讓)。餓死:低優先級線程長期獲取不到資源/CPU。死鎖:線程阻塞不變化。
程序順序、鎖規則、volatile 規則、線程啟動、線程終止、對象終結、傳遞性。保證「寫入對讀取可見」與「有序性」。
編譯器重排、CPU 重排 (LoadLoad/StoreStore/LoadStore/StoreLoad)。volatile 插入內存屏障、synchronized 隱含屏障、Unsafe.fullFence()。
ConcurrentHashMap (JDK 8/17) 實現原理?數組 + 鏈表/紅黑樹。CAS + synchronized (節點首部) 細粒度分段鎖。擴容支持多線程協助 (ForwardingNode)。size() 用 LongAdder 統計。computeIfAbsent 原子複合操作。
ThreadPoolExecutor 核心參數與執行流程?見文章 ID 168692 詳細解析。核心:core/max、隊列選型決定模型、Worker 循環 getTask、拒絕策略、動態調整。
Future、CompletableFuture、CompletionStage 關係?Future 阻塞獲取結果。CompletableFuture 實現 Future + CompletionStage,支持鏈式編排 (thenApply/thenCompose/thenCombine/allOf/anyOf)、異常處理 (exceptionally/handle)、自定義執行器。
ForkJoinPool 工作竊取算法?雙端隊列 (Deque),任務拆分遞歸,線程從自己隊列頭取任務,空時隨機竊取其他線程隊列尾任務。適用遞歸可分任務 (排序、搜索、流處理)。
BlockingQueue 實現對比?見 ID 168692 表格。核心:put/take 阻塞,offer/poll(timeout) 超時,remainingCapacity。
ReadWriteLock / StampedLock 適用場景?讀多寫少。ReentrantReadWriteLock 讀鎖共享、寫鎖獨佔、支持降級 (寫→讀) 不支持升級。StampedLock (JDK 8) 樂觀讀 (tryOptimisticRead/validate)、無重入、性能更高。
同步集合:Collections.synchronizedXxx 全鎖、性能差。並發集合:ConcurrentHashMap/CopyOnWriteArrayList/ConcurrentLinkedQueue 分段鎖/CAS/複製於寫、高並發吞吐強。
CopyOnWriteArrayList 缺點?寫入複製數組 (O(n))、內存佔用雙倍、數據一致性為最終一致性 (讀舊快照)、不適用寫多場景。
CPU 密集:核心數 +1。IO 密集:核心數 * (1 + 平均等待/平均工作)。實測調優:壓測觀察 CPU/GC/隊列/延遲。
execute() vs submit()?execute 無返回、異常拋給 UncaughtExceptionHandler。submit 返回 Future、異常包裝在 Future.get() 拋出。
shutdown vs shutdownNow?shutdown 不接受新任務、處理完隊列。shutdownNow 嘗試中斷執行中任務、返回未執行列表。標準關閉模式: shutdown() → awaitTermination(timeout) → shutdownNow()。
1. 停止接受外部請求 (限流/熔斷) 2. shutdown() 3. awaitTermination 等待存量 4. 超時 shutdownNow() 強制 5. 記錄未完成任務入庫補償。
CompletableFuture 異常處理最佳實踐?鏈路中任意節點異常 → 後續 thenApply 跳過 → exceptionally/handle 捕獲恢復 → 後續繼續。**注意:** whenComplete 不改變結果類型,handle 可轉換結果。
專用單線程/有界隊列線程池 + DiscardOldestPolicy + 異步 Appender (Log4j2/Logback AsyncAppender)。
1. jstack 看線程狀態 (RUNNABLE/BLOCKED/WAITING) 2. 監控指標 (隊列長度、活躍數、完成數、拒絕數) 3. 業務執行耗時分布 (before/afterExecute 統計) 4. 檢查鎖競爭、慢 SQL、下游超時。
CPU 從線程 A 切換到 B:保存 A 恢復 B (寄存器、程序計數器、棧指針)。減少:① 無鎖並發 (CAS/ThreadLocal) ② 縮小同步塊 ③ 協程/虛擬線程 (JDK 21) ④ 合理線程池大小避免過多線程。
ThreadPoolExecutor 如何實現定時/周期任務?用 ScheduledThreadPoolExecutor,內部 DelayedWorkQueue (堆) + ScheduledFutureTask (time/period/sequence)。
用戶態線程 (Continuation),由平台線程 (Carrier Thread) 載體運行。阻塞時讓出 Carrier (unmount),非阻塞繼續。優勢:百萬級併發、代碼同步寫法、無池化需求、簡化 IO 密集型編程。
① wait/notify (教學) ② BlockingQueue (標準) ③ Semaphore (信號量) ④ PipedInputStream/OutputStream (管道) ⑤ Disruptor (高性能環形隊列)。
讀操作加讀鎖,寫操作加寫鎖 (雙重檢查 + 寫鎖更新 + 失效緩存)。StampedLock 樂觀讀更優。
public class Singleton {
private static volatile Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton(); // volatile 禁止指令重排 (分配內存→初始化→賦值)
}
}
}
return instance;
}
}
JVM 保證類加載線程安全、天然防反射/序列化破壞、代碼最簡潔。
jstack PID 含 "Found one Java-level deadlock";JConsole/JVisualVM/JMC 圖形化檢測;代碼中 ThreadMXBean.findDeadlockedThreads()。
1. 監控線程數持續增長 2. jstack 分析線程名/狀態/堆疊 3. 命名線程池定位業務 4. 檢查 ThreadLocal 未清理、線程池未關閉、非守護線程未結束。
ThreadLocal 在線程池中使用注意事項?線程復用導致值殘留。**必須** 在任務結束 finally 中 remove()。或用 TransmittableThreadLocal (阿里開源) 支持父子線程傳遞。
Future.get(timeout)、CompletableFuture.orTimeout()、Future.cancel(true) (中斷)、CountDownLatch.await(timeout)。
無 notify 線程被喚醒 (OS/CPU 機制):**永遠在 while 循環中調用 wait()**,檢查條件滿足才退出。
Class.forName、static final、static{}、CallOnce (Guava)、AtomicReference CAS、ClassValue (JDK 8+)。
映射 OS 優先級 (1-10),**不保證調度順序**,受 OS 調度器支配。不建議依賴優先級控制業務邏輯。
synchronized 膨脹流程圖、AQS 隊列結構圖、ThreadPoolExecutor 執行流程圖jstack/jmap/jstat/arthas 線上排查實戰案例多線程面試考察的不僅是 API 記憶,更是併發思維、工程判斷、故障經驗。建議以「原理→源碼→實戰→排查」四維建立知識體系,重點攻克 JMM、AQS、線程池、CAS、鎖優化五座大山。面試時敢於說「不知細節但知查找方向」,勝過生硬背誦。祝你 Offer 滿懷!