HOWTO · Kotlin

在 Kotlin 中使用 Sleep 暫停執行緒執行

了解如何使用 Thread.sleep 或 TimeUnit 暫停 Kotlin/JVM 執行緒,以及何時改用 delay 或 ScheduledExecutorService。

本頁內容

同步 Kotlin/JVM 程式碼需要暫停目前執行緒時,請使用 Thread.sleep。需要以名稱清楚表示時間單位時,請使用 TimeUnit.sleep。在協程中使用 delay,即可暫停協程而不阻塞執行緒;如果工作應稍後執行,請改用 ScheduledExecutorService。任何休眠中的執行緒都可能被中斷,而延遲只是最短等待時間,並非精確的執行時間。

coroutine 是表示非阻塞執行單位的技術術語。

使用 Thread.sleep 阻塞目前執行緒

Thread.sleep(milliseconds) 是暫停呼叫執行緒的直接 JVM 操作。參數是代表毫秒數的 Long,方法會拋出 InterruptedException。正式程式碼應恢復中斷旗標或傳遞中斷,而不是只列印堆疊追蹤。

fun main() {
    println("before")
    try {
        Thread.sleep(100L)
        println("after")
    } catch (exception: InterruptedException) {
        Thread.currentThread().interrupt()
        println("interrupted")
    }
}

輸出:

before
after

從 Thread 使用 Kotlin Sleep 函式

這會讓呼叫執行緒至少阻塞約 100 毫秒,但不會停止整個 JVM,其他可執行的執行緒仍可繼續執行。若工作可以表示為非阻塞的暫停,請避免在事件迴圈或資源有限的工作執行緒中使用它。

使用 TimeUnit.sleep 表示易讀的單位

java.util.concurrent 中的 TimeUnit 會在休眠前轉換秒或毫秒等具名單位。它仍會阻塞目前執行緒並回應中斷,但 TimeUnit.SECONDS.sleep(1) 比直接進行毫秒換算更容易檢閱。

import java.util.concurrent.TimeUnit

fun main() {
    println("before")
    try {
        TimeUnit.MILLISECONDS.sleep(100)
        println("after")
    } catch (exception: InterruptedException) {
        Thread.currentThread().interrupt()
        println("interrupted")
    }
}

輸出:

before
after

從 TimeUnit 使用 Kotlin Sleep 函式

請選擇符合需求的單位。由於排程、競爭或作業系統計時器精度的影響,執行緒可能較晚恢復,因此轉換不保證精確時間。

在協程中使用 delay

delaykotlinx-coroutines-core 提供的暫停函式。它會暫停協程而不阻塞底層執行緒,讓其他協程可以使用該執行緒。它必須從暫停函式或協程作用域呼叫,不是一般同步程式碼中 Thread.sleep 的替代品。

使用 Kotlin delay 函式

例如,runBlocking 作用域可以啟動呼叫 delay(100L) 後列印訊息的協程。訊息順序取決於排程;取消也是重要邊界,因為 delay 可取消,被取消的協程通常會在暫停點停止。編譯協程程式碼前請加入 kotlinx-coroutines-core 相依性,Kotlin 標準函式庫本身不提供 delay

使用 ScheduledExecutorService 排程工作

當需求是「稍後執行此工作」而非「讓目前執行緒休眠」時,請使用 ScheduledExecutorService。排程工作可以在執行前取消,Executor 不再需要時應關閉。

import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

fun main() {
    val executor = Executors.newSingleThreadScheduledExecutor()
    val task = executor.schedule(
        { println("task ran") },
        100,
        TimeUnit.MILLISECONDS,
    )

    task.get()
    executor.shutdown()
}

輸出:

task ran

這裡的 get() 只是讓簡短的命令列範例保持執行,直到工作完成。在應用程式中,需要取消工作時請保留傳回的 ScheduledFuture,並依照生命週期策略使用 shutdownshutdownNow。排程不是精確的即時保證。

應該選擇哪種 Kotlin Sleep 方法?

  • 目前執行緒應有意阻塞的小型同步 JVM 操作使用 Thread.sleep
  • 需要以易讀單位表達相同阻塞行為時使用 TimeUnit.sleep
  • 協程中的可取消、非阻塞暫停使用 delay
  • 延遲後需要觸發獨立工作時使用 ScheduledExecutorService

不要將 wait() 當作一般休眠操作。Java 的 Object.wait() 參與監視器協調並要求適當的監視器,它解決的是不同的同步問題。無論選擇哪個 API,都應將中斷與取消視為正常控制流程,而非意外錯誤。