上文確立Thread對象與OS線程的區別。本文深度解析Thread/Runnable核心方法、join()語義、start()一次性限制、Runnable共享狀態模式,為後續同步、協作、線程池奠基。
| 維度 | 繼承Thread | 實現Runnable |
|---|---|---|
| 線程對象與任務 | 強耦合(同一對象) | 解耦(任務獨立) |
| 單繼承限制 | 佔用 | 無影響 |
start()次數 | 一次(對象綁定線程) | 多次(同一Runnable可包裝多Thread) |
| 共享狀態 | 需額外字段/外部對象 | 天然共享Runnable實例字段 |
| 線程池適配 | 不直接支持 | 完美適配ExecutorService |
| 函數式編程 | 不支持 | Lambda() -> {}、方法引用 |
結論:優先Runnable,配合ExecutorService或Thread.ofVirtual()(Java 21+虛擬線程)。
// Thread.java (JDK 17)
public synchronized void start() {
if (threadStatus != 0) // NEW=0
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0(); // native
started = true;
} finally {
if (!started) group.threadStartFailed(this);
}
}
threadStatus從NEW(0)→RUNNABLE→TERMINATED單向流轉。**二次start()拋IllegalThreadStateException**。
public final void join() throws InterruptedException {
join(0);
}
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) throw new IllegalArgumentException();
if (millis == 0) {
while (isAlive()) wait(0); // 死等
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) break;
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
關鍵點:
t.join()的線程(當前線程)阻塞在t對象監視器上t線程結束時,JVM調用t.notifyAll()喚醒等待者join(millis)帶超時,超時返回不拋異常,需檢查t.isAlive()判斷是否結束synchronized(t)塊中調用(方法聲明synchronized),否則IllegalMonitorStateException
class CounterTask implements Runnable {
private int count = 0; // 共享狀態!
@Override public void run() {
for (int i = 0; i < 10000; i++) count++;
System.out.println(Thread.currentThread().getName() + ": " + count);
}
}
public class SharedRunnable {
public static void main(String[] args) throws InterruptedException {
CounterTask task = new CounterTask();
Thread t1 = new Thread(task, "T1");
Thread t2 = new Thread(task, "T2");
t1.start(); t2.start();
t1.join(); t2.join();
System.out.println("Final: " + task.count); // 非20000!
}
}
現象:count++非原子(讀-改-寫),兩線程競爭導致更新丟失,結果<20000。
修復:
AtomicInteger:atomicCount.incrementAndGet()synchronized:synchronized(this) { count++; }ThreadLocal:各線程獨立副本,最後聚合| 方法 | 靜態/實例 | 作用 | 注意事項 |
|---|---|---|---|
currentThread() | 靜態 | 獲取當前執行線程對象 | 核心API,用於ThreadLocal、日誌、監控 |
sleep(millis/nanos) | 靜態 | 當前線程定時讓出CPU | 不釋放鎖、拋InterruptedException需處理 |
yield() | 靜態 | 提示調度器重新調度 | 不保證讓步、平台差異大、慎用 |
interrupt() | 實例 | 設置中斷標誌 | 不強制停止,阻塞方法收到InterruptedException |
isInterrupted() | 實例 | 檢查中斷標誌(不清除) | 用於輪詢判斷 |
interrupted() | 靜態 | 檢查並清除當前線程中斷標誌 | 唯一清除標誌的方法 |
setDaemon(true) | 實例 | 標記為守護線程 | 必須在start()前調用,JVM僅剩守護線程退出 |
getState() | 實例 | 獲取線程狀態(NEW/RUNNABLE/BLOCKED/WAITING/TIMED_WAITING/TERMINATED) | 監控、診斷用 |
getStackTrace() | 實例 | 獲取線程棧幀快照 | 定位死鎖、卡頓、性能分析 |
setUncaughtExceptionHandler() | 實例/靜態 | 設置未捕獲異常處理器 | 線程池建議設置默認處理器 |
public class DaemonDemo {
public static void main(String[] args) {
Thread daemon = new Thread(() -> {
while (true) {
System.out.println("Daemon running...");
try { Thread.sleep(500); } catch (InterruptedException e) { break; }
}
});
daemon.setDaemon(true); // 必須在start()前
daemon.start();
System.out.println("Main ends"); // 主結束,JVM退出,daemon被強制終止
}
}
規則:JVM進程在所有非守護線程結束後退出,不等待守護線程。典型用途:GC、JIT、監控、心跳、清理任務。
本文掌握:
下一站:同步與協作核心——synchronized、wait/notify、Lock、Condition、內存模型、volatile、原子類。