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,都应把中断和取消视为正常控制流,而不是意外错误。