Kotlin coroutines take turns on one thread

Read the explanation
What if

Two coroutines share the main thread. Each delay() suspends one and frees the thread for the other.

Predict first

Two coroutines share one thread and call delay() after each of their 3 steps. In what order do they print?

012345678runBlocking: launch a, blaunchwaits for its childrenstep 1coroutine a: delay: suspendeddelaystep 2coroutine a: delay: suspendeddelaystep 3delaycoroutine b: ready, waits for mainreadystep 1coroutine b: delay: suspendeddelaystep 2coroutine b: delay: suspendeddelaystep 3delay

Tap any block to see what that thread is doing.

  • Running
  • Waiting / parked
Step 1/4

runBlocking runs both coroutines on one thread: main.

1fun main() = runBlocking<Unit> { // one thread runs both coroutines2    launch { work("a") }3    launch { work("b") }4}5 6suspend fun work(name: String) {7    repeat(3) { step ->8        println("$name step ${step + 1} on ${Thread.currentThread().name}")9        delay(10) // suspends: the thread is free for the other coroutine10    }11}

Explanation

What just happened

runBlocking ran both coroutines on one thread, main. Coroutine a printed a step, then delay() suspended it. A suspended coroutine holds no thread, so b ran its step, then suspended too. They took turns at every delay(): a1 b1 a2 b2 a3 b3, all on main.

Try the what-if switch

Why it matters

Coroutines are cheap because many of them share a few threads. That only works if they suspend instead of blocking. One blocking call holds a thread that other coroutines were counting on.

Watch out

Blocking calls don’t look different from suspending ones in the code. Calls like JDBC, file reads and Thread.sleep block; run them on Dispatchers.IO.

Common questions

Do Kotlin coroutines run on separate threads?

Not necessarily. In runBlocking, both coroutines here ran on the main thread, taking turns. Which threads run a coroutine depends on its dispatcher.

What's the difference between delay() and Thread.sleep() in a coroutine?

delay() suspends the coroutine and frees its thread for other coroutines. Thread.sleep() blocks the thread itself, so no other coroutine can run on it until it returns.

When does a coroutine give up its thread?

At a suspension point, like delay(), join() or await(). Between two suspension points, a coroutine keeps its thread.

Verified: this timeline matches a real program, checked in CI on Kotlin 2.4 with kotlinx.coroutines 1.11. How we verify. Unclear or wrong? Tell us