Coroutines vs threads mental model in Kotlin - Performance Comparison
When working with coroutines and threads in Kotlin, it is important to understand how their execution costs grow as we increase workload.
We want to see how the time taken changes when we run many tasks using coroutines versus threads.
Analyze the time complexity of launching multiple tasks using threads and coroutines.
fun runThreads(n: Int) {
repeat(n) {
Thread {
// some work
}.start()
}
}
suspend fun runCoroutines(n: Int) {
repeat(n) {
kotlinx.coroutines.GlobalScope.launch {
// some work
}
}
}
This code runs n tasks either by creating n threads or launching n coroutines.
Look at what repeats as input size grows.
- Primary operation: Creating and running
ntasks (threads or coroutines). - How many times: Exactly
ntimes, once per task.
As we increase n, the number of tasks grows linearly.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 tasks created and run |
| 100 | 100 tasks created and run |
| 1000 | 1000 tasks created and run |
Pattern observation: The work grows directly with the number of tasks.
Time Complexity: O(n)
This means the time to start and run all tasks grows in a straight line as we add more tasks.
[X] Wrong: "Coroutines always run faster than threads because they are lightweight."
[OK] Correct: While coroutines use fewer resources, the total time depends on the work done and how tasks are scheduled, not just on their lightweight nature.
Understanding how coroutines and threads scale helps you explain efficient multitasking in Kotlin, a skill useful in many programming situations.
What if we changed the tasks to do heavy CPU work instead of light work? How would the time complexity change?
Practice
Solution
Step 1: Understand management of coroutines and threads
Coroutines are managed by Kotlin's runtime, making them lightweight tasks.Step 2: Understand thread management
Threads are managed by the operating system and are heavier compared to coroutines.Final Answer:
Coroutines are lightweight and managed by Kotlin, threads are heavier and managed by the OS. -> Option CQuick Check:
Coroutines lightweight, threads OS-managed = A [OK]
- Confusing which manages coroutines vs threads
- Thinking coroutines are OS threads
- Assuming threads are lightweight like coroutines
Solution
Step 1: Identify coroutine launch syntax
The correct way to start a coroutine in Kotlin is using the 'launch' builder inside a coroutine scope.Step 2: Check other options for correctness
Thread { println("Hello from thread") }.start() starts a thread, not a coroutine. runBlocking { Thread.sleep(1000) } uses runBlocking with Thread.sleep, which blocks a thread. coroutineStart { println("Start") } is invalid syntax.Final Answer:
launch { println("Hello from coroutine") } -> Option DQuick Check:
launch starts coroutine = A [OK]
- Using Thread instead of launch for coroutines
- Confusing runBlocking with coroutine launch
- Using invalid coroutine syntax
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
delay(100)
println("Coroutine finished")
}
println("Main finished")
}Solution
Step 1: Understand runBlocking and launch behavior
runBlocking blocks the main thread until its coroutine completes. The launch coroutine delays 100ms before printing.Step 2: Trace output order
"Main finished" prints immediately. After 100ms delay, "Coroutine finished" prints. runBlocking waits for launch to complete before exiting.Final Answer:
Main finished Coroutine finished -> Option AQuick Check:
runBlocking waits, delay delays launch = C [OK]
- Assuming launch runs before main print
- Ignoring delay effect
- Thinking runBlocking doesn't wait
fun main() {
launch {
println("Hello")
}
}Solution
Step 1: Check coroutine launch context
launch must be called inside a coroutine scope or suspend function. Here, main is a regular function without scope.Step 2: Identify missing coroutine scope
Without a scope like runBlocking or CoroutineScope, launch cannot start a coroutine.Final Answer:
Missing coroutine scope to launch coroutine -> Option AQuick Check:
launch needs coroutine scope = B [OK]
- Trying to launch coroutine outside scope
- Replacing launch with Thread incorrectly
- Thinking println causes error
Solution
Step 1: Understand concurrency with coroutines
Using async inside coroutines allows concurrent execution and returns Deferred results.Step 2: Compare options for efficiency and correctness
Create multiple threads and use Thread.sleep to wait for results uses threads and blocking sleep, which is heavy and inefficient. Use a single coroutine and call blocking network functions sequentially blocks sequentially, losing concurrency. Run network calls on the main thread directly blocks main thread, causing UI freeze.Final Answer:
Launch multiple coroutines with async and await their results -> Option BQuick Check:
async + await = concurrent coroutines [OK]
- Using threads with sleep instead of coroutines
- Blocking main thread with network calls
- Running network calls sequentially inside one coroutine
Ready to prove it?
Pass the test and this pattern moves unknown
