Java 線程分兩類:
Thread thread = new Thread(() -> { ... });
thread.setDaemon(true); // 必須在 start() 前調用
thread.start();
// 判斷是否守護線程
boolean isDaemon = thread.isDaemon();
關鍵規則:
setDaemon(true) 必須在 start() 之前調用,否則拋出 IllegalThreadStateExceptionfinally 塊可能不會執行public class DaemonDemo {
public static void main(String[] args) throws IOException {
Thread daemon = new Thread(() -> {
int i = 0;
try {
while (true) {
System.out.println("Daemon running: " + i++);
Thread.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
System.out.println("Daemon finally block"); // 可能不執行
}
});
daemon.setDaemon(true);
daemon.start();
System.out.println("Main thread sleeping 500ms...");
Thread.sleep(500);
System.out.println("Main thread exits -> JVM shuts down");
// 此時守護線程被強制終止,finally 不會執行
}
}
public class CacheCleaner {
private final Map<String, CacheEntry> cache = new ConcurrentHashMap<>();
private final Thread cleanerThread;
public CacheCleaner(long intervalMs) {
cleanerThread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(intervalMs);
cleanExpired();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
cleanerThread.setDaemon(true); // 應用關閉時自動停止
cleanerThread.start();
}
private void cleanExpired() {
long now = System.currentTimeMillis();
cache.entrySet().removeIf(e -> e.getValue().isExpired(now));
}
public void shutdown() {
cleanerThread.interrupt(); // 优雅關閉
}
}
Thread metricsCollector = new Thread(() -> {
while (true) {
try {
collectAndReportMetrics(); // 發送到 Prometheus/InfluxDB
Thread.sleep(10_000);
} catch (InterruptedException e) {
break;
}
}
});
metricsCollector.setDaemon(true);
metricsCollector.start();
框架內部的心跳檢測、連接清理、內存監控線程通常設置為守護線程,避免阻止應用關閉。
| 場景 | 推薦類型 | 理由 |
|---|---|---|
| 業務處理、HTTP 請求、任務執行 | 用戶線程 | 必須完成工作,不能被強制中斷 |
| 緩存清理、監控上報、心跳檢測 | 守護線程 | 輔助功能,主程序退出時無需強制完成 |
| 數據持久化、事務提交 | 用戶線程 | 數據一致性關鍵,需確保完成 |
| 日誌異步寫入、指標聚合 | 守護線程 + 緩衝隊列 | 允許少量數據丟失,換取關閉速度 |
shutdown() 方法調用 interrupt(),實現優雅停機Executors.newCachedThreadPool() 創建的線程默認是用戶線程;自定義 ThreadFactory 可設置守護屬性Runtime.getRuntime().addShutdownHook() 而非依賴守護線程 finallyQ: GC 線程是守護線程嗎?
A: 是。JVM 內部的 GC、Finalizer、Reference Handler、JIT 編譯線程均為守護線程。當只有這些線程存活時,JVM 退出。
Q: 如何讓守護線程在 JVM 退出前完成關鍵工作?
A: 不推薦依賴守護線程做關鍵工作。改用用戶線程 + 關閉鉤子,或將任務提交給專用的非守護線程池並等待完成。