Thread.yield() 是一個靜態方法,其作用是:提示調度器當前線程願意放棄佔用的 CPU 時間片,從「運行狀態」回到「就緒狀態」。
「排隊上廁所,輪到你時突然說:『來比賽誰先衝進去!』所有人回到起跑線。你可能再次贏得,也可能別人搶先。有優先權(高優先級)的人獲勝概率大,但不保證。」
public class YieldDemo extends Thread {
public YieldDemo(String name) {
super(name);
}
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println(getName() + " -> " + i);
// 當計數到 30 時主動讓步
if (i == 30) {
System.out.println(">>> " + getName() + " 在 " + i + " 處調用 yield()");
Thread.yield();
}
}
}
public static void main(String[] args) {
new YieldDemo("張三").start();
new YieldDemo("李四").start();
}
}
張三 -> 1 ... 29
張三 -> 30
>>> 張三 在 30 處調用 yield()
李四 -> 1 ... 50 // 李四完整執行完畢
張三 -> 31 ... 50 // 張三繼續
李四 -> 1 ... 30
>>> 李四 在 30 處調用 yield()
李四 -> 31 ... 50 // 調度器忽略提示,李四繼續執行
張三 -> 1 ... 50
張三 -> 1..10
李四 -> 1..10
張三 -> 11..20
李四 -> 11..20
... yield 點附近可能發生上下文切換 ...
// JVM 內部實現簡化
JVM_ENTRY(void, JVM_Yield(JNIEnv* env, jclass threadClass))
JVMWrapper("JVM_Yield");
// 調用操作系統讓步系統調用
os::yield();
JVM_END
| OS | 系統調用 | 行為 |
|---|---|---|
| Linux | sched_yield() | 將進程移至就緒隊列尾部 |
| Windows | SwitchToThread() / Sleep(0) | 嘗試切換到同優先級線程 |
| macOS | pthread_yield_np() | 類似 Linux |
// 錯誤示例:嘗試用 yield 等待標誌位
while (!flag) {
Thread.yield(); // 極其不可靠,可能導致 CPU 100% 空轉
}
// 正確做法:使用 wait/notify、Lock、CountDownLatch 等同步機制
調度器傾向於運行高優先級線程。yield 僅讓位於同優先級的就緒線程。
現代 JVM (JIT 編譯器、偏向鎖、逃逸分析) 已極度優化。盲目添加 yield 反而增加上下文切換開銷,降低吞吐量。
| 方法 | 釋放鎖 | 狀態轉換 | 可中斷 | 典型用途 |
|---|---|---|---|---|
| yield() | 否 | RUNNABLE → RUNNABLE | 否 | 提示調度器 |
| sleep(ms) | 否 | RUNNABLE → TIMED_WAITING | 是 (InterruptedException) | 定時延遲 |
| wait() | 是 | RUNNABLE → WAITING | 是 | 線程協作/同步 |
| join() | 否 | RUNNABLE → WAITING | 是 | 等待線程結束 |
| LockSupport.park() | 否 | RUNNABLE → WAITING | 是 | 底層阻塞工具 |
在 JDK 5+ 並發包成熟後,幾乎不需要直接使用 yield():
ReentrantLock, CountDownLatch, Semaphore, CompletableFutureBlockingQueue, Phaser, ExchangerScheduledExecutorServiceForkJoinPool, parallelStreamThread.yield() 是一個諮詢式、非確定性的調度提示,而非控制指令。理解其「可能生效、可能無效、不釋放鎖、不保證公平」的本質,避免在業務邏輯中依賴它進行同步或流程控制。現代 Java 開發中,應優先選擇 java.util.concurrent 包提供的高級並發工具,讓專業的調度器處理線程協作。