集成開發環境(IDE)的圖形化調試器擅長單線程、斷點式調試,但面對高併發的 Java 服務器程序(如 Web 容器、消息隊列、緩存中間件)往往無能為力。此時,線程轉儲 是唯一能「凍結」整個 JVM 運行狀態、無需修改代碼、無需重啟服務的「X 光片」。
| 方式 | 命令/操作 | 適用場景 |
|---|---|---|
| 控制台快捷鍵 | Ctrl + \\ (Linux/Mac) / Ctrl + Break (Windows) | 前台運行的開發/測試環境 |
| JDK 工具 | jstack <pid> / jcmd <pid> Thread.print | 生產環境、後台進程、腳本自動化 |
| 程序化觸發 | Thread.getAllStackTraces() + 寫入日誌 | 定時巡檢、異常自動捕獲、嵌入監控系統 |
Full thread dump:
"Thread-1" prio=5 tid=0x87fe0 nid=0x800013b8 waiting on monitor [0..0xbfffe9f8]
"Thread-0" prio=5 tid=0xafde0 nid=0x72a00 runnable [0x173f000..0x173fb48]
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:421)
at java.net.ServerSocket.implAccept(ServerSocket.java:243)
at java.net.ServerSocket.accept(ServerSocket.java:222)
at com.example.net.PortListener.run(PortListener.java:38)
at java.lang.Thread.run(Thread.java:496)
"Signal Dispatcher" daemon prio=10 tid=0xa5f80 nid=0x52af0 waiting on monitor [0..0]
"Finalizer" daemon prio=8 tid=0xa46c0 nid=0x4d930 waiting on monitor [0x10a8000..0x10a8b48]
at java.lang.Object.wait(Native Method)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:108)
...
"Reference Handler" daemon prio=10 tid=0xa3b70 nid=0x4cca0 waiting on monitor [0x1027000..0x1027b48]
at java.lang.Object.wait(Native Method)
...
"VM Thread" prio=5 tid=0xa2de0 nid=0x4ca20 runnable
"VM Periodic Task Thread" prio=10 tid=0xa56b0 nid=0x52890 waiting on monitor
"Thread-0" —— 默認命名,無業務含義,難以定位。prio=5 —— 1~10,影響調度但非決定性。runnable / waiting on monitor / blocked / timed_waiting 等。| 狀態 | 含義 | 典型原因 |
|---|---|---|
| RUNNABLE | 正在執行或就緒 | CPU 密集計算、I/O 等待(網絡/磁盤)、鎖競爭 |
| BLOCKED | 等待進入 synchronized 區塊 | 鎖競爭嚴重、長耗時同步方法 |
| WAITING | 無限期等待 notify/join | Object.wait()、Thread.join()、LockSupport.park() |
| TIMED_WAITING | 帶超時的等待 | Thread.sleep()、wait(timeout)、Future.get(timeout) |
默認名稱 Thread-0、Thread-1 在百線程系統中毫無意義。兩種命名方式:
// ❌ 無業務語義
Thread t = new Thread(runnable);
// ✅ 清楚標識職責
Thread t = new Thread(runnable, "Order-Processing-Worker-1");
Thread t = new Thread(runnable, "Netty-Server-Boss-Group");
Thread t = new Thread(runnable, "Kafka-Consumer-Group-OrderService");
Thread.currentThread().setName("Dynamic-Task-" + taskId);
// 或在線程池中使用 ThreadFactory
ThreadFactory factory = r -> {
Thread t = new Thread(r, "Custom-Pool-" + counter.incrementAndGet());
t.setDaemon(true);
return t;
};
ExecutorService pool = Executors.newFixedThreadPool(10, factory);
// 命名前
"Thread-0" prio=5 ... runnable
at com.example.ServerSocket.accept(...)
// 命名後
"Order-Processing-Worker-1" prio=5 ... runnable
at com.example.ServerSocket.accept(...)
一眼就能定位:這是訂單處理線程,正在接受網絡連接。
JDK 自帶死鎖檢測,Dump 末尾會顯示:
Found one Java-level deadlock:
=============================
"Thread-A":
waiting to lock monitor 0x... (object 0x..., a java.lang.Object)
held by "Thread-B"
"Thread-B":
waiting to lock monitor 0x... (object 0x..., a java.lang.Object)
held by "Thread-A"
直接定位到鎖對象與代碼行號。
BLOCKED 或 WAITING 狀態的業務線程。jstack -l <pid> > dump_$(date +%s).txt 定時腳本線程轉儲是 Java 生產環境故障診斷的「核磁共振」。養成給線程池、核心業務線程起有業務語義名字的習慣,配合 jstack/jcmd 定期巡檢,能將 80% 以上的併發問題(死鎖、饑餓、活鎖、CPU 熱點、IO 等待)在分鐘級定位。下一步,結合 Java Flight Recorder (JFR) 或 Async Profiler 進行火焰圖分析,可實現微秒級性能優化。