首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 多線程與並發面試題精選 25 題(含詳細解析)

     編輯:Java 相關知識

前言

多線程與並發是 Java 面試必考重點,也是高併發系統設計的基石。本文精選 25 道經典面試題,涵蓋線程基礎、同步機制、JUC 包、線程池、死鎖等核心知識點,附帶深度解析,助你構建完整知識體系。

一、線程基礎篇

1. 進程與線程的區別?

進程:資源分配的基本單位,擁有獨立記憶體空間(堆、棧、方法區),上下文切換開銷大。
線程:CPU 調度的基本單位,隸屬於進程,共享進程堆/方法區,擁有獨立程式計數器、棧幀、本地變數。線程間通信更輕量(共享記憶體)。

2. 多線程編程的優勢?

  • 提高 CPU 利用率(IO 密集型任務等待時切換執行)
  • 共享堆記憶體,數據交換高效
  • 響應性更好(GUI、Server 不阻塞主線程)

3. 用戶線程 vs 守護線程?

用戶線程:JVM 不會因其結束而退出(如 main 線程)。
守護線程:為用戶線程服務(GC、JIT 編譯),所有用戶線程結束 JVM 即退出。
thread.setDaemon(true) 必須在 start() 前調用。

4. 創建線程的兩種方式?

  • 實現 Runnable / Callable 接口,傳入 Thread 構造器(推薦,解耦任務與執行)
  • 繼承 Thread 類並重寫 run()

5. 線程生命週期六大狀態?

NEW → RUNNABLE(就緒/運行) → BLOCKED(鎖等待) → WAITING/TIMED_WAITING(無限/限時等待) → TERMINATED。
注意:RUNNABLE 包含就緒與運行,JVM 不區分。

6. 直接調用 run() vs start()?

run() 只是普通方法調用,仍在當前線程執行。start() 會請求 JVM 創建新線程,異步執行 run()。

7. sleep() vs wait()?

sleep()wait()
所屬類Thread(靜態)Object
釋放鎖否是
喚醒方式時間到自動notify/notifyAll/interrupt
調用前提無必須持有對象鎖(同步塊/方法)

8. 線程優先級有用嗎?

優先級只是提示,不保證 高優先級先執行。受 OS 調度器影響大,Java 不建議依賴優先級控制業務邏輯。

9. 什麼是上下文切換?

CPU 從一個線程切換到另一個線程,需保存/恢復程式計數器、棧幀、寄存器狀態。頻繁切換會嚴重影響性能(可用 vmstat、pidstat 觀測)。

二、同步與協作篇

10. synchronized 原理?

基於監視器鎖。每個對象有 monitor,monitorenter/monitorexit 字節碼指令實現。JDK 6+ 引入偏向鎖、輕量級鎖、鎖消除、鎖粗化等優化。

11. synchronized 方法 vs 同步塊?

同步塊更好:可精確控制鎖範圍,減少鎖持有時間,降低爭用。同步方法鎖住整個對象(或類對象)。

12. volatile 關鍵字作用?

  • 可見性:寫入立即刷新到主存,讀取強制從主存載入
  • 禁止指令重排序(JMM happens-before 規則)
  • 不保證原子性(如 i++ 仍需同步或 AtomicInteger)

13. wait/notify/notifyAll 為什麼定義在 Object?

每個對象天然擁有監視器鎖,線程通信本質是「等待/通知對象的鎖狀態」。若定義在 Thread,則每個線程需綁定特定鎖,靈活性差。

14. 為什麼 wait/notify 必須在同步塊中?

調用 wait() 會釋放鎖並進入等待隊列;notify() 會喚醒等待隊列線程,但需當前線程釋放鎖才能讓被喚醒線程獲取鎖。同步塊保證當前線程持有鎖。

15. ThreadLocal 原理與應用?

每個線程持有 ThreadLocalMap<ThreadLocal, Object>,Key 為 ThreadLocal 實例(弱引用),Value 為線程局部變數。
應用:資料庫連線、Session 上下文、事務管理、日期格式化 SimpleDateFormat(非線程安全)。
注意:使用後必須 remove(),否則線程池場景會導致記憶體洩漏。

三、進階並發篇

16. 什麼是原子操作?JUC 原子類?

不可分割的操作單元。AtomicInteger、AtomicLong、AtomicReference、AtomicStampedReference(解決 ABA 問題)、LongAdder(高併發計數器,分段累加)。

17. Lock 接口優於 synchronized 的地方?

  • 可中斷鎖等待:lockInterruptibly()
  • 可嘗試非阻塞獲取:tryLock()
  • 可設定超時:tryLock(timeout, unit)
  • 公平鎖/非公平鎖可選
  • 多條件變量:newCondition() 支援多個等待隊列
  • 可在不同作用域獲取/釋放鎖

18. Executor 框架核心組件?

  • Executor:頂層接口,execute(Runnable)
  • ExecutorService:生命週期管理、提交任務返回 Future
  • ThreadPoolExecutor:核心實現,7 大參數控制
  • Executors:工廠方法(生產禁用,OOM 風險)
  • ScheduledExecutorService:定時/周期任務

19. 線程池 7 大參數?

new ThreadPoolExecutor(
    corePoolSize,      // 核心線程數
    maximumPoolSize,   // 最大線程數
    keepAliveTime,     // 非核心線程存活時間
    unit,              // 時間單位
    workQueue,         // 任務隊列(有界/無界)
    threadFactory,     // 線程工廠(命名、守護屬性)
    handler            // 拒絕策略
)

四種拒絕策略:AbortPolicy(預設拋異常)、CallerRunsPolicy(調用者執行)、DiscardPolicy(丟棄)、DiscardOldestPolicy(丟棄最舊)。

20. 死鎖產生條件與排查?

四條件:互斥、佔有並等待、不可搶佔、循環等待。
排查:jstack pid 生成線程快照,尋找 BLOCKED 狀態線程及其等待的鎖(waiting for <0x...>),定位持有鎖的線程。

21. 如何避免死鎖?

  • 固定鎖獲取順序(如按 hashCode 排序)
  • 使用 tryLock(timeout) 避免無限等待
  • 減少鎖持有範圍,避免嵌套鎖
  • 使用並發容器替代手動同步

22. Callable vs Runnable?

Callable<V> 有返回值、可拋出異常,配合 Future<V> 獲取結果。Runnable 無返回值。

23. FutureTask 作用?

FutureTask 實現 RunnableFuture,既是 Future 又是 Runnable。可包裝 Callable 提交給線程池,也可自定義計算邏輯(如緩存計算結果,避免重複計算)。

24. 並發容器有哪些?

  • ConcurrentHashMap:JDK 8 CAS + synchronized + 紅黑樹,分段鎖升級為節點鎖
  • CopyOnWriteArrayList/Set:讀多寫少場景,寫入時複製底層陣列
  • ConcurrentLinkedQueue:無鎖隊列,CAS 實現
  • BlockingQueue 實現:ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue、DelayQueue

25. 如何獲取線程轉儲?

  • jstack -l pid > dump.txt(推薦,JDK 自帶)
  • kill -3 pid(Linux/Unix,輸出到標準輸出)
  • VisualVM / JConsole / Arthas 圖形化工具

四、總結與建議

多線程並發知識體系龐大,建議按「基礎 → 同步 → JUC → 實戰調優」四階段學習。面試時重點掌握:synchronized/JMM/volatile 原理、線程池參數與拒絕策略、死鎖排查、ConcurrentHashMap 演進、ThreadLocal 記憶體洩漏。實戰中優先使用高層抽象(並發容器、線程池、CompletableFuture),少寫底層同步代碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved