Kotlin Coroutines vs Threads | Concurrency Model, Cost, and When to Use Which

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

  1. Quick comparison
  2. How they work
  3. Performance
  4. Memory
  5. Structured concurrency
  6. Practical guide
  7. Side-by-side example
  8. Closing thoughts

1. Quick comparison

CoroutinesThreads
WeightMany thousands+Dozens–hundreds typical
Memory~KB-scale state~MB stacks
Create costVery lowHigher (OS)
Context switchUser-space (cheap)Kernel (heavier)
CancellationStructured + cooperativeManual / interrupt
DefaultPreferLegacy / 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

  1. Default to coroutines on the JVM with Kotlin
  2. Threads for narrow legacy/interop cases
  3. Pick Dispatchers for I/O vs CPU
  4. Use structured concurrency for lifecycles

Coroutines are not magic—they organize async work safely on top of threads.


  • Kotlin coroutines guide
  • Kotlin Dispatchers
  • Structured concurrency

Keywords

Kotlin, coroutine, thread, async, concurrency, Dispatchers, structured concurrency, comparison