多線程是 Java 後端面試核心模塊。本文整理 15 道高頻題,提供標準答案、底層原理、代碼片段、避坑指南,並補充 11 道進階補充題,構建完整知識圖譜。
t1.start(); t1.join(); t2.start(); t2.join(); t3.start(); 或 CountDownLatch 鏈式。ReentrantReadWriteLock)、可中斷(lockInterruptibly)、公平鎖、嘗試獲取(tryLock)、多條件(Condition)。緩存場景用 ReadWriteLock 或 StampedLock(樂觀讀)。wait() 釋放鎖、需在同步塊中、notify 喚醒;sleep() 持有鎖、靜態方法、時間到自醒。wait 用於線程協作,sleep 用於暫停。ReentrantLock + 兩個 Condition(notFull/notEmpty),put()/take() 循環等待+單次喚醒。生產用 ArrayBlockingQueue。wait/notify 版、BlockingQueue 版、Semaphore 版。synchronized(A){synchronized(B){}} vs synchronized(B){synchronized(A){}}。排查:jstack 發現 BLOCKED、死鎖檢測;預防:固定鎖順序、tryLock(timeout)、LockSupport.parkNanos。AtomicInteger (CAS)、LongAdder (分段計數)、synchronized、volatile (單次讀寫)。jstack、ThreadSanitizer、壓測。解決:鎖、原子類、線程封閉、不可變對象。kill -3 <pid>、Windows Ctrl+Break、jstack <pid>。關注:RUNNABLE(CPU 熱點)、BLOCKED(鎖競爭)、WAITING(條件等待)、死鎖標記。start() 創建新線程、異步執行 run();直接調用 run() 同步執行在當前線程,無新線程。sleep/wait/join → interrupt() 拋 InterruptedException;LockSupport.park → unpark。reset())、攔截點阻塞、可執行屏障動作;後者一次性、計數歸零觸發、無屏障動作。String、Integer、LocalDateTime 皆不可變。tryLock 重試)、飢餓(公平鎖解決)、上下文切換開銷。| 題目 | 核心點 |
|---|---|
| 綠色線程 vs 本地線程 | 綠色:用戶態調度(早期 JVM);本地:OS 內核調度(現代 JVM) |
| 線程 vs 進程 | 進程:資源分配單位、獨立地址空間;線程:調度單位、共享進程資源 |
| 上下文切換 | 保存/恢復 PCB(程序計數器、棧指針、寄存器)、切換頁表、刷新 TLB |
| 死鎖 vs 活鎖 vs 饑餓 | 死鎖:互等永久阻塞;活鎖:主動重試但無進展;飢餓:長期搶不到資源 |
| 線程調度算法 | 時間片輪轉 + 優先級搶佔(OS 相關),Java 僅提示優先級 |
| 線程調度 | OS 內核調度器決定哪個線程獲得 CPU 時間片 |
| 非捕獲異常處理 | Thread.setDefaultUncaughtExceptionHandler、ThreadGroup.uncaughtException |
| 線程組 | 已棄用,結構脆弱、無實際隔離,用線程池替代 |
| Executor 框架優勢 | 生命週期管理、任務佇列、拒絕策略、監控、可調參數、分離提交/執行 |
| Executor vs Executors | 前者介面;後者工廠類(newFixedThreadPool 等),阿里規範禁用工廠方法,自定義參數 |
| 定位高 CPU 線程 | top -H -p <pid> 獲取 LWP ID → printf "%x" LWP → jstack 找十六進制 nid |
// 題目:如何實現線程安全的單例?
// 回答要點:
// 1. 枚舉單例(最推薦,防反射/序列化破壞)
// 2. 靜態內部類(延遲加載、線程安全)
// 3. DCL + volatile(注意指令重排)
// 4. 說明為何懶漢同步方法效能差、饿漢可能浪費空間