HOWTO · Kotlin

How to Sleep to Suspend a Thread's Execution in Kotlin

Learn how to pause Kotlin/JVM thread code with Thread.sleep or TimeUnit, and when to use coroutine delay or ScheduledExecutorService instead.

On this page

Use Thread.sleep when synchronous Kotlin/JVM code must pause the current thread. Use TimeUnit.sleep when a named time unit makes the delay clearer. For coroutines, use delay so the coroutine suspends without blocking its thread; for a task that should run later, use ScheduledExecutorService instead. Every sleeping thread can be interrupted, and a delay is a minimum wait rather than an exact execution time.

Use Thread.sleep to block the current thread

Thread.sleep(milliseconds) is the direct JVM operation for pausing the thread that calls it. The argument is a Long number of milliseconds, and the method throws InterruptedException. A production caller should restore the interrupt flag or propagate the interruption instead of silently printing a stack trace.

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

Output:

before
after

Using Kotlin sleep function from Thread

This blocks the calling thread for at least approximately 100 milliseconds. It does not make the whole JVM stop: other runnable threads may continue. Avoid it on an event-loop or a limited worker thread when the work could be expressed as a non-blocking suspension.

Use TimeUnit.sleep for readable units

TimeUnit from java.util.concurrent converts a named unit such as seconds or milliseconds before sleeping. It still blocks the current thread and still responds to interruption, but TimeUnit.SECONDS.sleep(1) is easier to review than a literal millisecond conversion.

import java.util.concurrent.TimeUnit

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

Output:

before
after

Using Kotlin sleep function from TimeUnit

Choose the unit that matches the requirement. The conversion does not guarantee exact timing: the thread may resume later because of scheduling, contention, or operating-system timer resolution.

Use delay inside a coroutine

delay is a suspending function from kotlinx-coroutines-core. It pauses the coroutine without blocking the underlying thread, so another coroutine can use that thread. It must be called from a suspending function or a coroutine scope; it is not a replacement for Thread.sleep in ordinary synchronous code.

Using Kotlin delay function

For example, a runBlocking scope can launch a coroutine that calls delay(100L) and then prints a message. The order of that message depends on scheduling, while cancellation is an important boundary: delay is cancellable, so a cancelled coroutine normally stops at the suspension point. Add the kotlinx-coroutines-core dependency before compiling coroutine code; the Kotlin standard library alone does not provide delay.

Schedule work with ScheduledExecutorService

Use ScheduledExecutorService when the requirement is “run this task later” rather than “make this current thread sleep.” A scheduled task can be cancelled before it runs, and the executor should be shut down when it is no longer needed.

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()
}

Output:

task ran

get() is used here only to keep the short command-line example alive until the task completes. In an application, retain the returned ScheduledFuture when you need to cancel the task, and use shutdown or shutdownNow according to the application’s lifecycle policy. Scheduling is not a precise real-time guarantee.

Which Kotlin sleep approach should you choose?

  • Use Thread.sleep for a small, synchronous JVM operation where blocking the current thread is intentional.
  • Use TimeUnit.sleep for the same blocking behavior with a readable unit.
  • Use coroutine delay for cancellable, non-blocking suspension inside coroutines.
  • Use ScheduledExecutorService when a separate task should be triggered after a delay.

Do not use wait() as a general sleep operation. Java’s Object.wait() participates in monitor coordination and requires the appropriate monitor; it solves a different synchronization problem. Whichever API you choose, treat interruption and cancellation as normal control-flow outcomes rather than as unexpected errors.