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
这会让调用线程至少阻塞大约 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
请选择符合需求的单位。由于调度、竞争或操作系统计时器精度的影响,线程可能更晚恢复,因此换算不保证精确的时间。
在协程中使用 delay
delay 是 kotlinx-coroutines-core 提供的挂起函数。它暂停协程而不阻塞底层线程,因此其他协程可以使用该线程。它必须从挂起函数或协程作用域调用,并不是普通同步代码中 Thread.sleep 的替代品。
例如,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,并根据应用生命周期策略使用 shutdown 或 shutdownNow。调度并不是精确的实时保证。
应该选择哪种 Kotlin Sleep 方法?
- 当前线程有意阻塞的小型同步 JVM 操作使用
Thread.sleep。 - 需要用清晰单位表达相同阻塞行为时使用
TimeUnit.sleep。 - 协程中的可取消、非阻塞挂起使用
delay。 - 需要在延迟后触发独立任务时使用
ScheduledExecutorService。
不要把 wait() 当作通用休眠操作。Java 的 Object.wait() 参与监视器协调并要求正确的监视器,它解决的是不同的同步问题。无论选择哪种 API,都应把中断和取消视为正常控制流,而不是意外错误。