Coroutines vs threads

Coroutines Junior Kotlin 2.0+ API 24+ Reviewed By Best Latest Apps

Predict the output

Roughly how long does this take on a normal laptop?

fun main() = runBlocking {
    val start = System.currentTimeMillis()
    val jobs = List(10_000) {
        launch { delay(1_000) }
    }
    jobs.joinAll()
    println(if (System.currentTimeMillis() - start < 3_000) "about 1 s" else "much longer")
}
Options
Hint 1 of 2

Does delay block the thread?

Hint 2 of 2

How many threads does runBlocking use here?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A About 1 second

Correct. All 10,000 coroutines suspend at the same time on one thread, so they finish together.

Why not the others?
  • B About 10,000 seconds. That would be true if each delay blocked the only thread in turn. delay suspends instead.
  • C It runs out of memory. Suspended coroutines are small heap objects; 10,000 is nothing.
  • D It never finishes because runBlocking has one thread. One thread is enough because nobody blocks it.

30-second answer

A coroutine is a suspendable unit of work that runs on threads but does not own one. When it suspends, it gives its thread back so other coroutines can run there. That makes coroutines cheap enough to start thousands of them, while each thread costs memory and kernel scheduling.

Practise saying it · 40 s. No recording, just a timer

Explain how a coroutine differs from a thread.

Say your answer out loud, then check which of these points you covered:

  • Coroutines run on threads but don't own one
  • Suspending releases the thread
  • Cheap enough for thousands
  • Blocking calls still block the thread

Deep dive

A dispatcher decides which threads a coroutine runs on. One coroutine may resume on a different thread from the one it started on.

Suspending saves the coroutine's state in a heap object (a continuation) instead of blocking a stack. That is why delay costs nothing while Thread.sleep blocks a thread.

Switching between coroutines still happens, but it is a user-space resume, not a kernel thread switch.

Coroutines are cooperative: a coroutine stuck in a CPU loop without suspension points never yields its thread.

Common mistakes

  • Saying coroutines run without any threads.
  • Calling blocking code like Thread.sleep or blocking I/O inside a coroutine on a small dispatcher.
  • Thinking launch always creates a new thread.

Senior take & follow-ups

A good answer explains suspension in one sentence (the thread is released, the state is saved) and then gives the practical result: cheap concurrency, but you must not block.

Follow-up questions

Think of your answer first, then open the hint.

  1. What happens if you start 100,000 coroutines that each call delay(1000)?

    Show hint

    It finishes in about a second with little memory; 100,000 threads would not.

  2. Can a coroutine move between threads?

    Show hint

    Yes, on a multi-thread dispatcher it may resume on any of its threads.

Suggest a fix if something here is wrong or out of date.