skip to content

How Kotlin Does Coroutines

A suspend function is compiled into a state machine that hands control back rather than parking a thread, which is why coroutines are cheap enough to launch by the thousand. Structured concurrency then ties each coroutine's lifetime to a parent scope, so nothing outlives its owner.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

questions

5

What is a Kotlin coroutine, and how does it differ from a thread?

level: juniorimportance: must knowfreq 85%

answer

  1. Suspendable resumable computation, user-space
  2. Thread = heavy OS resource; coroutine = heap object
  3. Suspend frees the thread, block holds it
  4. delay vs Thread.sleep
  5. Many coroutines multiplex over few threads

basics

~10 s

A coroutine is a lightweight task that can pause and resume. Unlike a thread, it does not block an OS thread while waiting, so you can run very many of them at once cheaply.

solid answer

~40 s

A coroutine is a unit of suspendable computation managed in user space by the kotlinx.coroutines library, not by the OS. A thread is a heavy OS resource (megabytes of stack, scheduled by the kernel); a coroutine is just an object on the heap. When a coroutine hits a suspension point (a call to a `suspend` function that actually suspends, like `delay`), it gives up its thread instead of blocking it, and resumes later — possibly on a different thread. This means thousands or millions of coroutines can multiplex over a small thread pool. Coroutines run inside a `CoroutineScope`, are launched with builders like `launch`/`async`, and are dispatched onto threads by a `CoroutineDispatcher`. The slogan is "coroutines are like lightweight threads" — concurrency without one-thread-per-task cost.

code

kotlin · 10 lines
kotlin
import kotlinx.coroutines.*

fun main() = runBlocking {
    val job = launch {
        delay(500)            // suspends; thread is reused meanwhile
        println("resumed on ${Thread.currentThread().name}")
    }
    println("launched, not blocked")
    job.join()
}

go deeper

for a junior

Can state a coroutine is a lightweight, pausable task and that it doesn't block a thread while waiting.

for a middle

Explains user-space multiplexing over a thread pool and the suspend-vs-block distinction with delay vs Thread.sleep.

for a senior

Frames it as library + compiler support, ties lifetime to scope/dispatcher, and reasons about memory/scheduling cost.

for a principal

Discusses when coroutines genuinely beat threads vs not (CPU-bound work), and the system-design implications of cheap concurrency.

## What a coroutine is A **coroutine** is a computation that can be **suspended** (paused) and later **resumed**, without blocking the underlying thread it was running on. It is implemented entirely as a library (`kotlinx.coroutines`) plus a small amount of compiler support — there is no special OS or JVM primitive behind it. Think of it as a resumable function: when it needs to wait for something (a network call, a timer, another coroutine), it stores its state, returns the thread to the pool, and is scheduled to continue later from exactly where it left off. ## How it differs from a thread - **Thread**: an OS-managed execution context. It has a large stack (often ~0.5–1 MB), is scheduled preemptively by the kernel, and blocking it (e.g. `Thread.sleep`) wastes that whole resource while it waits. - **Coroutine**: a plain heap object managed in **user space**. Suspending is cooperative, not preemptive. Many coroutines share a few threads. ```kotlin import kotlinx.coroutines.* fun main() = runBlocking { // 100k coroutines — fine. 100k threads — would crash. repeat(100_000) { launch { delay(1000) // suspends, does NOT block a thread print(".") } } } ``` `delay(1000)` is a **suspending** function: it parks the coroutine and frees the thread for 1 second, unlike `Thread.sleep(1000)` which holds the thread hostage. ## Key vocabulary - **Suspension point**: a place where a coroutine may pause — only inside `suspend` functions. - **CoroutineScope**: defines a coroutine's lifetime and tracks its children. - **Coroutine builder**: `launch`, `async`, `runBlocking` — functions that actually start a coroutine. - **Dispatcher** (`CoroutineDispatcher`): decides which thread(s) the coroutine runs on (e.g. `Dispatchers.Default`, `Dispatchers.IO`). ## Why this matters Because suspension frees the thread instead of blocking it, you get high concurrency (lots of waiting tasks) at very low memory and scheduling cost — the core reason coroutines exist.

  • Does each coroutine own its own thread?
    No. Coroutines are multiplexed over a (usually small) pool of threads by a dispatcher; a single thread runs many coroutines, and one coroutine may resume on a different thread than it started on.
  • Is suspending the same as blocking?
    No. Blocking holds the thread idle; suspending releases the thread so other coroutines can use it, then resumes later.

Threads are dedicated phone lines; coroutines are call-waiting on a few shared lines — when one call pauses, another uses the line.

saying these in an interview costs you the question

  • Says a coroutine is just a thread with another name
  • Claims each coroutine has its own OS thread
  • Confuses suspend with block / uses Thread.sleep inside coroutines
  • Thinks the JVM has native coroutine support rather than a library + compiler

context

open as a page

What is structured concurrency, and how does the parent Job/scope tie coroutine lifetimes together?

level: middleimportance: must knowfreq 72%

basics

~20 s

Structured concurrency means every coroutine runs inside a scope, and a parent waits for all its children to finish. If the parent is cancelled, its children are cancelled too — so nothing is left running or leaked.

open as a page

How does the Kotlin compiler implement a `suspend` function under the hood?

level: seniorimportance: must knowfreq 70%

basics

~20 s

The compiler rewrites a suspend function into a state machine. Each pause point becomes a state, and the function is given a hidden parameter that lets it remember where to continue from when it resumes.

open as a page

Why are coroutines described as 'cheap', and what does that buy you compared to a thread-per-task model?

level: middleimportance: should knowfreq 60%

basics

~10 s

Each coroutine is just a small object, not an OS thread, so creating one barely costs memory. That lets you have huge numbers of waiting tasks at once without exhausting threads.

open as a page

Why did Kotlin implement coroutines as a compiler transform plus a library rather than as runtime fibers or extra threads? What trade-offs does that bring?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

Kotlin builds coroutines from a small compiler feature plus a library, instead of changing the runtime. This keeps them portable and cheap, but means they only pause at marked points and you must use suspend-aware libraries.

open as a page