finally 塊可能不執行);模擬:3 個生產者線程每秒產生事件,守護線程每輪掃描刪除 10 秒前的過期事件。
class Event {
private final long timestamp = System.currentTimeMillis();
private final String message;
Event(String msg) { this.message = msg; }
long getTimestamp() { return timestamp; }
String getMessage() { return message; }
}
class EventProducer implements Runnable {
private final Deque queue;
EventProducer(Deque q) { this.queue = q; }
@Override public void run() {
for (int i = 0; i < 100; i++) {
Event e = new Event("Event-" + Thread.currentThread().getId() + "-" + i);
queue.addFirst(e); // 頭插,尾部最舊
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException ex) { break; }
}
}
}
class EventCleaner extends Thread {
private final Deque queue;
EventCleaner(Deque q) {
this.queue = q;
setDaemon(true); // 關鍵:設置為守護線程
setName("EventCleaner-Daemon");
}
@Override public void run() {
while (true) { // 無限循環,JVM 退出自動終止
cleanExpired();
try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { break; }
}
}
private void cleanExpired() {
long now = System.currentTimeMillis();
boolean removed = false;
// 從尾部(最舊)開始檢查
while (!queue.isEmpty()) {
Event oldest = queue.peekLast();
if (now - oldest.getTimestamp() > 10_000) {
queue.removeLast();
System.out.printf("[Cleaner] Removed: %s%n", oldest.getMessage());
removed = true;
} else break; // 隊列有序,遇到未過期即可停止
}
if (removed) System.out.printf("[Cleaner] Queue size: %d%n", queue.size());
}
}
public class DaemonDemo {
public static void main(String[] args) {
Deque queue = new ArrayDeque<>();
// 3 個生產者
for (int i = 0; i < 3; i++) new Thread(new EventProducer(queue), "Producer-" + i).start();
// 1 個守護清理器
new EventCleaner(queue).start();
// 主線程結束,非守護線程(Producer)還在跑,JVM 不退出
// 若將 Producer 也設為守護,主線程結束即全部退出
}
}
start() 前調用 setDaemon(true),啟動後修改拋 IllegalThreadStateException;Runtime.getRuntime().addShutdownHook() 或 Spring SmartLifecycle,確保非守護線程完成收尾工作。
// 替代手寫守護線程,支持優雅關閉、固定頻率/延遲、異常處理
ScheduledExecutorService cleaner = Executors.newSingleThreadScheduledExecutor(r -> {
Thread t = new Thread(r, "CacheCleaner");
t.setDaemon(true); // 仍可設置守護
return t;
});
cleaner.scheduleAtFixedRate(() -> {
// 掃描清理邏輯
}, 10, 10, TimeUnit.SECONDS);
// 應用關閉時
cleaner.shutdown();
cleaner.awaitTermination(5, TimeUnit.SECONDS);
守護線程是「犧牲自己成全主線程」的利他者。適用於非關鍵、周期性、可重試、無鎖依賴的後台維護任務。生產代碼建議用 ScheduledExecutorService 替代手寫 Thread,獲得標準化生命周期管理與異常處理。