多執行緒程式設計中,執行緒之間的協作與控制是核心課題。Java 提供了豐富的 API 讓開發者精確掌控執行緒生命週期、調度順序與資源競爭。本文深度解析四大核心控制機制:join 等待合併、Daemon 守護執行緒、sleep/yield 讓出 CPU與執行緒優先級,附完整程式碼範例與執行行為分析。
呼叫 `thread.join()` 會讓當前執行緒(通常是主執行緒)進入阻塞狀態,直到目標執行緒 `thread` 執行結束(正常結束或拋出異常)為止。常用於「分治法」:主執行緒分派子任務,待所有子任務完成後再匯總結果。
public class JoinThreadTest extends Thread {
public JoinThreadTest(String name) { super(name); }
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
if (i == 20) {
JoinThreadTest joinThread = new JoinThreadTest("JoinThread");
joinThread.start();
try {
joinThread.join(); // 主執行緒阻塞,等待 JoinThread 跑完 0~99
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + " " + i);
}
}
}
public class DaemonThreadTest extends Thread {
public DaemonThreadTest(String name) { super(name); }
@Override
public void run() {
for (int i = 0; i < 1000; i++) {
System.out.println(getName() + " " + i);
}
}
public static void main(String[] args) {
DaemonThreadTest daemon = new DaemonThreadTest("DaemonThread");
daemon.setDaemon(true); // 必須在 start() 前
daemon.start();
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
}
// 主執行緒跑完 0~99 結束,JVM 退出,DaemonThread 只能輸出部分數字
}
}
| 特性 | sleep(millis) | yield() |
|---|---|---|
| 狀態轉換 | RUNNING → TIMED_WAITING(阻塞) | RUNNING → RUNNABLE(就緒) |
| CPU 佔用 | 睡眠期間完全不佔用 CPU | 僅暫時讓出,調度器可能立即再次選中 |
| 影響範圍 | 所有優先級執行緒 | 僅影響優先級 ≥ 當前執行緒的執行緒 |
| 異常處理 | 拋 InterruptedException,需 try-catch | 不拋受檢異常 |
| 典型用途 | 定時輪詢、延遲重試、模擬耗時操作 | 禮讓同優先級執行緒、避免佔用過久 |
public class YieldTest extends Thread {
public YieldTest(String name) { super(name); }
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println(getName() + " " + i);
if (i == 20) Thread.yield();
}
}
public static void main(String[] args) {
YieldTest high = new YieldTest("High");
high.setPriority(Thread.MAX_PRIORITY); // 10
high.start();
YieldTest low = new YieldTest("Low");
low.setPriority(Thread.MIN_PRIORITY); // 1
low.start();
// 結果:High 執行到 i=20 yield,但因優先級高,調度器大概率再次選中 High 繼續跑完
// 若註解掉 setPriority,兩者優先級相同,yield 後 Low 獲得執行機會機率大增
}
}
| 需求 | 推薦 API | 備註 |
|---|---|---|
| 主執行緒等待子任務完成 | join() / CountDownLatch / CompletableFuture | 現代開發優先考慮高層併發工具 |
| 背景非關鍵任務 | Daemon Thread / ScheduledExecutorService | 避免阻塞 JVM 關閉 |
| 固定間隔/延遲執行 | sleep() / ScheduledExecutorService | sleep 精度受 OS 影響,約 10-15ms |
| 禮讓 CPU 給同優先級執行緒 | yield() | 極少直接使用,通常由鎖/同步器隱含處理 |
| 調整執行緒重要性 | setPriority() | 僅作微調,不可作為同步手段 |
掌握 `join`、`Daemon`、`sleep/yield` 與優先級的語義差異與適用邊界,是編寫高併發、高穩定性 Java 程式的基石。實務中,優先使用 `java.util.concurrent` 套件提供的高層抽象(`ExecutorService`、`Future`、`CompletableFuture`、`CountDownLatch`、`Semaphore` 等),僅在極簡單場景或框架底層直接操作 `Thread` 原生 API,能顯著降低併發 Bug 風險並提升程式碼可維護性。