Coroutines
Kotlin's concurrency model: suspend functions that release the thread instead of blocking it, builders that start work, dispatchers that decide where it runs, and a Job hierarchy that makes cancellation and cleanup structural. Any backend or Android interview that touches async work spends most of its time here.
part ofKotlinoverview, primer and where to startread it →on this pageshowhide
explore
- How Kotlin Does Coroutines5 questions
- Suspension Mechanics15 questions
- suspend Functions5 questions
- CPS & State-Machine Compilation5 questions
- suspendCancellableCoroutine Callback Bridge5 questions
- Coroutine Builders25 questions
- launch (fire-and-forget)5 questions
- async / await5 questions
- runBlocking5 questions
- coroutineScope vs supervisorScope5 questions
- withContext5 questions
- Context & Dispatchers15 questions
- CoroutineContext & Elements5 questions
- Dispatchers (Default/IO/Main/Unconfined)5 questions
- Thread Confinement & limitedParallelism5 questions
- Structured Concurrency25 questions
- Structured Concurrency Principle5 questions
- Parent/Child Job Hierarchy5 questions
- SupervisorJob5 questions
- Scope Lifecycle & Leak Prevention5 questions
- coroutineScope Cancellation Semantics5 questions
- Cancellation19 questions
- Cooperative Cancellation5 questions
- isActive / ensureActive / yield5 questions
- NonCancellable Cleanup4 questions
- withTimeout / withTimeoutOrNull5 questions
- Exception Handling20 questions
- Exception Propagation5 questions
- CoroutineExceptionHandler5 questions
- Supervisor vs Job Error Handling5 questions
- async Exception Deferral5 questions
- Synchronization Primitives20 questions
- Mutex & Semaphore5 questions
- Channel Basics5 questions
- select Expression5 questions
- produce & actor Builders5 questions
questions
144 · 8 sectionsWhat is a Kotlin coroutine, and how does it differ from a thread?
basics
~10 sA 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.
What is structured concurrency, and how does the parent Job/scope tie coroutine lifetimes together?
basics
~20 sStructured 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.
How does the Kotlin compiler implement a `suspend` function under the hood?
basics
~20 sThe 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.
Why are coroutines described as 'cheap', and what does that buy you compared to a thread-per-task model?
basics
~10 sEach 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.
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?
basics
~20 sKotlin 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.
What is suspendCancellableCoroutine and when do you use it?
basics
~10 sIt is a helper that turns an old-style callback API into a suspend function. You pause the coroutine, then call resume in the callback to continue with a result, or resumeWithException to fail.
What does the Kotlin compiler actually do to a `suspend` function under the hood? Describe the Continuation parameter it adds.
basics
~20 sThe compiler rewrites a suspend function to take a hidden extra parameter representing 'what to do next'. When the function pauses, it hands work back to that callback and resumes later instead of blocking a thread.
What does the `suspend` modifier do, and where can you call a `suspend` function from?
basics
~10 ssuspend marks a function that can pause and resume later without blocking its thread. You can only call it from a coroutine or from another suspend function.
What is invokeOnCancellation for, and what are the rules around resuming after cancellation?
basics
~10 sinvokeOnCancellation runs a cleanup block when the coroutine is cancelled while suspended, so you can stop the underlying work. After cancellation any value you try to resume with is ignored, so close it safely.
How does the compiler use a state machine and a `label` field to resume a suspend function with multiple suspension points?
basics
~20 sThe compiler splits the function at each pause point into numbered states. A label counter remembers which state to run next, so when the function resumes it jumps straight to the right spot instead of starting over.
What does the async coroutine builder return, and how do you get the result out of it?
basics
~10 sasync starts a background task and hands you a Deferred. You call await() on it to wait for and receive the result value.
What does CoroutineScope.launch do, and what does it return?
basics
~10 slaunch starts a new coroutine that runs in the background without giving you a result. It returns a Job, a handle you can use to wait for it to finish or to cancel it.
What is runBlocking in Kotlin coroutines, and when should you use it?
basics
~10 srunBlocking is a bridge that lets regular blocking code call suspend functions. It blocks the current thread until the coroutines inside finish. Use it in main() and tests, not inside other coroutines.
What does the suspending function coroutineScope do, and how is it different from launch or runBlocking?
basics
~10 scoroutineScope starts a block where you can run several coroutines and it waits for all of them to finish before returning. It does not block the thread; it only suspends.
What does withContext do, and how would you use it to run blocking I/O off the main thread?
basics
~20 swithContext runs a block of suspending code on a different thread pool and waits for its result. You wrap blocking I/O in withContext(Dispatchers.IO) so it does not freeze the main thread, then it returns the value.
What is a CoroutineContext in Kotlin coroutines, and what kind of data does it hold?
basics
~20 sIt is a small set of settings carried by every coroutine. It bundles things like which thread pool runs the coroutine, its Job (lifecycle), and an optional name, so each coroutine knows how it should run.
What is a CoroutineDispatcher, and when would you use Dispatchers.Default versus Dispatchers.IO?
basics
~10 sA dispatcher decides which thread a coroutine runs on. Use Default for heavy CPU work like sorting or parsing, and IO for waiting on network or disk calls.
What does Dispatcher.limitedParallelism(n) do, and why would you use it?
basics
~20 sIt creates a view of a dispatcher that lets at most n coroutines run at the same time. You use it to cap how much work hits a limited resource, like a database or an external API.
Explain the Element/Key model of CoroutineContext: how does context[CoroutineName] resolve an element, and why is each element also a CoroutineContext?
basics
~20 sEach setting in the context is an Element tagged with a unique Key. You look one up by indexing with its key. Every single element is itself a tiny one-item context, which is why you can add them together with +.
How does the + operator combine coroutine contexts, and what are its precedence/replacement rules when the same kind of element appears twice?
basics
~20 sThe + operator merges two contexts into a new one. If both sides contain the same kind of element (say two dispatchers), the one on the right wins and replaces the one on the left.
What is a Job in Kotlin coroutines, and what are the states in its lifecycle?
basics
~10 sA Job is a handle to a running coroutine. It can be active, can complete, or can be cancelled. You can wait for it, cancel it, or check if it is still running.
What does the coroutineScope { } builder do, and how is it different from launching with GlobalScope?
basics
~20 scoroutineScope runs a block, starts child coroutines inside it, and waits until all of them finish before returning. GlobalScope launches coroutines that are not tied to anything and can outlive your code, so they can leak.
Why does an object with a lifecycle (e.g. a ViewModel, a service, a request handler) usually own its own CoroutineScope, and what does cancelling that scope accomplish?
basics
~20 sThe object creates a scope to launch background work tied to its life. When the object is done, it cancels the scope, which stops every coroutine started in it so nothing keeps running and leaking.
What is structured concurrency in Kotlin coroutines, and what core guarantee does it give you?
basics
~10 sEvery coroutine runs inside a scope, and that scope won't finish until all the coroutines it started have finished. This stops background work from being forgotten or leaking.
What is a SupervisorJob in Kotlin coroutines, and how does it differ from a regular Job when one child fails?
basics
~10 sA SupervisorJob is a special parent for coroutines. If one child crashes, the others keep running and the parent stays alive. With a regular Job, one crash cancels everything.
You launch a coroutine that runs a tight CPU-bound while-loop, then call cancel() on its Job. The loop keeps running. Why, and what is the simplest fix?
basics
~10 sCancellation is cooperative: the coroutine only stops if it checks for cancellation. A pure CPU loop never checks, so it ignores cancel(). Add a check inside the loop, such as while (isActive).
In Kotlin coroutines, what actually happens when you call Job.cancel()? Does the coroutine stop immediately?
basics
~10 sNo. cancel() just marks the job as cancelled. The coroutine actually stops the next time it pauses at a suspending call. If it never pauses, it keeps running.
Why can a suspending cleanup call inside a finally block fail to run after a coroutine is cancelled, and how does NonCancellable fix it?
basics
~10 sWhen a coroutine is cancelled, trying to suspend again (like awaiting a call) in finally immediately throws cancellation, so cleanup is skipped. Wrapping that cleanup in withContext(NonCancellable) lets it suspend and complete.
What do withTimeout and withTimeoutOrNull do in Kotlin coroutines, and how do they differ?
basics
~10 sBoth run a block of code with a time limit. If the work takes too long, withTimeout throws an error, while withTimeoutOrNull just returns null. Use them so slow operations don't hang forever.
Compare isActive, ensureActive() and yield() for making a loop cancellable. When would you pick each?
basics
~20 sisActive is a Boolean you test to exit the loop without throwing. ensureActive() throws CancellationException if cancelled. yield() throws too and also lets other coroutines run. Pick by whether you want to throw and/or reschedule.
When you call async { ... } and the block throws an exception, at what point is that exception observed by the caller?
basics
~10 sThe exception is stored inside the Deferred result and is re-thrown when you call await() on it. Calling async itself does not throw.
What is CoroutineExceptionHandler in Kotlin coroutines, and what kind of failures does it handle?
basics
~10 sIt is a piece of coroutine context that acts as a last-resort catcher. When a coroutine started with launch fails and nobody else handles the exception, this handler runs instead of crashing silently.
In Kotlin coroutines, what happens when a coroutine started with `launch` throws an uncaught exception?
basics
~10 sThe coroutine fails. The error travels up to its parent, which cancels itself and its other child coroutines, and then the error is reported.
What is the difference between a regular Job and a SupervisorJob when one of their child coroutines fails?
basics
~10 sWith a normal Job, if one child crashes, the parent and all the other children are cancelled too. With a SupervisorJob, a crashing child only affects itself; its siblings keep running.
Explain why a failing root async can cancel its entire scope even though its exception is only thrown at await(). How do deferral and structured concurrency interact here?
basics
~20 sawait() decides where YOU see the error. But the async is also a child coroutine, so when it fails it tells its parent Job, which cancels the parent and all siblings — regardless of whether you await.
What is a Channel<T> in Kotlin coroutines, and how do producer and consumer coroutines communicate through it?
basics
~10 sA Channel is a pipe between coroutines. One coroutine puts values in with send, another takes them out with receive. Both suspend (wait politely) until the other side is ready.
Why shouldn't you use the JVM `synchronized` block (or a `ReentrantLock`) to protect shared state inside a coroutine, and what does Kotlin offer instead?
basics
~10 ssynchronized blocks the whole thread while it waits. Coroutines share threads, so blocking one stalls others. Kotlin's Mutex instead suspends the coroutine, freeing the thread for other work.
Explain the Channel capacity options RENDEZVOUS, BUFFERED, CONFLATED, and UNLIMITED, and how each affects send.
basics
~10 sCapacity controls how many values a channel holds before send has to wait. Rendezvous holds none, buffered holds a few, unlimited holds everything, and conflated keeps only the latest and never waits.
Explain what `Mutex.withLock { }` does mechanically, including how it behaves on exceptions and cancellation. How does it differ from manual `lock()`/`unlock()`?
basics
~10 swithLock acquires the lock, runs your block, and always releases it afterward — even if the block throws or the coroutine is cancelled. Manual lock()/unlock() makes you remember to release it yourself.
Why does `produce` return a `ReceiveChannel` while `actor` returns a `SendChannel`, and what are the receiver scopes inside each?
basics
~20 sWith produce the coroutine makes values for you, so you get the read end (ReceiveChannel). With actor you feed it messages, so you get the write end (SendChannel). Each is the opposite end of the channel from what the coroutine uses.