← Back to Kotlin Course | Chapter 12: Coroutines | Lesson 4 of 7

Dispatchers

A dispatcher decides which pool of real worker threads a coroutine actually runs its work on.

What a Dispatcher Does

A dispatcher controls which thread (or thread pool) the coroutine's code runs on; different dispatchers are tuned for different kinds of work, like CPU computation versus blocking I/O.

Warning: This example uses kotlinx.coroutines, which is not part of the plain Kotlin standard library. It requires the kotlinx-coroutines-core dependency (added via Gradle/Maven) to compile and run.

Example: What a Dispatcher Does

markup
import kotlinx.coroutines.*

fun main() {
    runBlocking {
        launch(Dispatchers.Default) {
            println("Running on Dispatchers.Default: ${Thread.currentThread().name}")
        }
    }
}

Dispatchers.IO for Blocking Operations

Dispatchers.IO is backed by a larger thread pool designed to handle many concurrent blocking operations, such as file or network access, without exhausting the CPU-focused thread pool.

Warning: This example uses kotlinx.coroutines, which is not part of the plain Kotlin standard library. It requires the kotlinx-coroutines-core dependency (added via Gradle/Maven) to compile and run.

Example: Dispatchers.IO for Blocking Operations

markup
import kotlinx.coroutines.*

fun main() = runBlocking {
    withContext(Dispatchers.IO) {
        println("Simulating a blocking IO operation")
    }
    println("Back on the original context")
}

Switching Context with withContext

withContext(dispatcher) { } suspends the current coroutine, runs the given block on the specified dispatcher, and then resumes back with the block's result once it finishes.

Warning: This example uses kotlinx.coroutines, which is not part of the plain Kotlin standard library. It requires the kotlinx-coroutines-core dependency (added via Gradle/Maven) to compile and run.

Example: Switching Context with withContext

markup
import kotlinx.coroutines.*

suspend fun computeSum(): Int = withContext(Dispatchers.Default) {
    (1..1000).sum()
}

fun main() = runBlocking {
    println("Sum: ${computeSum()}")
}

Dispatchers.Default for CPU Work

Dispatchers.Default is sized based on the number of CPU cores and is meant for computationally intensive work, keeping it separate from dispatchers meant for blocking I/O.

Warning: This example uses kotlinx.coroutines, which is not part of the plain Kotlin standard library. It requires the kotlinx-coroutines-core dependency (added via Gradle/Maven) to compile and run.

Example: Dispatchers.Default for CPU Work

markup
import kotlinx.coroutines.*

fun main() = runBlocking {
    val result = withContext(Dispatchers.Default) {
        (1..100).map { it * it }.sum()
    }
    println("Sum of squares: $result")
}
Common Mistakes
  1. Using Dispatchers.Main outside an environment (like Android) that actually provides a main/UI thread dispatcher implementation.
  2. Doing heavy CPU or I/O work on Dispatchers.Default when Dispatchers.IO is designed and sized specifically for blocking I/O operations.
  3. Forgetting withContext suspends until the block finishes, and assuming it behaves like launch by returning immediately.
Chapter Summary
  • A dispatcher determines which thread or thread pool a coroutine's code actually executes on.
  • Dispatchers.Default is optimized for CPU-intensive work; Dispatchers.IO is optimized for blocking I/O operations.
  • withContext(dispatcher) { } switches the current coroutine to a different dispatcher for the duration of that block, then switches back.
  • Choosing the right dispatcher keeps CPU-heavy and I/O-heavy work from starving each other's thread pools.
🔒

Chapter Quiz — Complete all 7 topics to unlock

0/7 topics done

Complete these topics first:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.