Kotlin Coroutines vs Threads | Concurrency Model, Cost, and When to Use Which
이 글의 핵심
Kotlin coroutines vs threads: cost, cancellation, structured concurrency, and Dispatchers.
Introduction
“Coroutines or threads?” This article compares Kotlin coroutines and Java/OS threads and gives practical defaults.
What you will learn
- How each works (kernel vs user-level scheduling)
- Cost of creation and context switches
- Structured concurrency and cancellation
- When threads still appear
Table of contents
- Quick comparison
- How they work
- Performance
- Memory
- Structured concurrency
- Practical guide
- Side-by-side example
- Closing thoughts
1. Quick comparison
| Coroutines | Threads | |
|---|---|---|
| Weight | Many thousands+ | Dozens–hundreds typical |
| Memory | ~KB-scale state | ~MB stacks |
| Create cost | Very low | Higher (OS) |
| Context switch | User-space (cheap) | Kernel (heavier) |
| Cancellation | Structured + cooperative | Manual / interrupt |
| Default | Prefer | Legacy / special |
2. How they work
Threads: OS schedules, large stacks, kernel transitions.
Coroutines: suspend without blocking threads; delay frees the worker; state machines resume later—often on thread pools (Dispatchers).
3. Performance
Spawning many tasks: coroutines usually win. CPU-bound work still needs cores—use Dispatchers.Default or parallel streams appropriately.
4. Memory
Thousands of blocked threads can exhaust memory; thousands of suspended coroutines are far cheaper (still not infinite—watch leaks).
5. Structured concurrency
coroutineScope, parent Job, and structured cancellation propagate failures and cleanup—unlike fire-and-forget raw threads.
6. Practical guide
Use coroutines for:
- Network / DB (
Dispatchers.IO) - Parallel
async/await - Composable concurrency with clear scopes
Threads may remain for:
- Legacy Java pools
- Blocking code you cannot wrap yet
- Interop constraints
7. Code comparison
Manual Thread + synchronized lists vs async/awaitAll under coroutineScope—coroutine code is shorter and propagates errors uniformly.
Closing thoughts
- Default to coroutines on the JVM with Kotlin
- Threads for narrow legacy/interop cases
- Pick Dispatchers for I/O vs CPU
- Use structured concurrency for lifecycles
Coroutines are not magic—they organize async work safely on top of threads.
Related posts
- Kotlin coroutines guide
- Kotlin Dispatchers
- Structured concurrency
Keywords
Kotlin, coroutine, thread, async, concurrency, Dispatchers, structured concurrency, comparison