Java 線程調度行為由 JVM 實現決定,規範不強制要求搶佔式或時間片輪轉。本文基於《Java 線程調度》原著核心內容,深度剖析搶佔與時間片的區別、Solaris/Windows 平台差異、優先級語義的不可靠性、可運行池模型,以及開發者唯一可依賴的控制手段——wait() 與 sleep()。
| 平台 / JVM 實現 | 調度特徵 |
|---|---|
| Solaris (原生線程模型) | 同優先級線程不搶佔(協作式特徵),高優先級可搶佔低優先級。線程主動 yield()/sleep()/阻塞時才讓出 CPU。 |
| Windows (綠色線程/混合模型) | 使用時間片輪轉,同優先級甚至高優先級線程均可被搶佔。表現為強搶佔式。 |
| 現代 Linux (NPTL) / HotSpot JVM | 映射內核線程,採用 CFS 完全公平調度器,表現為時間片輪轉 + 動態優先級,行為接近 Windows。 |
規範承諾:跨 JVM 實現無絕對搶佔或時間片保證。唯一可移植的線程控制手段是 wait()、sleep()、join()、LockSupport.park() 等顯式同步/阻塞 API。
sleep()、yield()、wait()、IO 阻塞)。線程從可運行變為阻塞的典型原因:
Thread.sleep(ms):主動暫停指定時間,不釋放鎖。Object.wait():等待通知,**放鎖**進入等待集。synchronized、Lock.lock() 獲取不到鎖進入阻塞隊列。InputStream.read()、Socket.accept() 等原生阻塞調用。阻塞線程條件滿足(超時、被通知、鎖釋放、IO 就緒)後,重新進入可運行池等待調度。
既然調度器行為不可移植,正確的多線程程序必須基於顯式同步構建:
synchronized / ReentrantLock 保護共享數據。Object.wait/notify / Condition.await/signal / CountDownLatch / Semaphore 實現線程間條件等待。volatile / final / 鎖 保證內存可見性。volatile / 鎖 禁止指令重排。絕不使用 Thread.yield()、Thread.setPriority()、忙等待循環作為同步手段。
jstack、Java Mission Control、async-profiler 分析線程狀態、鎖競爭、CPU 熱點。java.util.concurrent 高級組件,將調度細節委託給經過驗證的框架實現。理解調度器的不確定性,才能編寫出真正健壯、可移植的 Java 併發程序。