Kotlin Coroutines and Concurrency Tutorial: Coroutine Basics, Suspend Functions, Scopes, and Dispatchers


This Kotlin Coroutines and Concurrency tutorial introduces coroutine fundamentals including what coroutines are, how suspend functions work, coroutine scopes, and dispatchers. With clear examples and best practices, this chapter helps developers write efficient, non-blocking, and concurrent Kotlin applications.

Coroutines and Concurrency – Coroutine Basics (Complete Tutorial)

What Are Coroutines?

Coroutines are lightweight threads that enable asynchronous and non-blocking programming. Unlike traditional threads, coroutines are efficient, cheap to create, and managed by the Kotlin runtime.

Why Use Coroutines?

  1. Simplify async code
  2. Avoid callback hell
  3. Improve performance and responsiveness

Coroutine Basics Example


import kotlinx.coroutines.*

fun main() = runBlocking {
launch {
delay(1000L)
println("Coroutine finished")
}
println("Main thread continues")
}

suspend Functions

suspend functions are functions that can suspend execution without blocking a thread.

Example


import kotlinx.coroutines.*

suspend fun fetchData(): String {
delay(1000L)
return "Data received"
}

fun main() = runBlocking {
val result = fetchData()
println(result)
}

Best Practices

  1. Use suspend for long-running or I/O operations.
  2. Call suspend functions only from coroutines or other suspend functions.

Coroutine Scopes

A coroutine scope defines the lifecycle of coroutines.

Common Scopes

  1. GlobalScope (not recommended)
  2. runBlocking
  3. CoroutineScope

Example


import kotlinx.coroutines.*

fun main() {
val scope = CoroutineScope(Dispatchers.Default)

scope.launch {
delay(500L)
println("Running in custom scope")
}

Thread.sleep(1000L)
}

Best Practices

  1. Avoid GlobalScope.
  2. Tie coroutine scopes to lifecycle (Android, server requests).

Dispatchers

Dispatchers determine which thread the coroutine runs on.

Common Dispatchers

  1. Dispatchers.Main – UI operations
  2. Dispatchers.IO – I/O tasks (file, network)
  3. Dispatchers.Default – CPU-intensive work
  4. Dispatchers.Unconfined – Not confined to a specific thread

Dispatcher Example


import kotlinx.coroutines.*

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

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

Best Practices for Dispatchers

  1. Use IO for blocking I/O tasks.
  2. Use Default for CPU-heavy computations.
  3. Switch contexts using withContext().

withContext(Dispatchers.IO) {
// I/O work
}

Summary

This chapter introduced Kotlin coroutines and concurrency basics, covering what coroutines are, suspend functions, coroutine scopes, and dispatchers. Understanding these fundamentals is essential for building scalable, responsive, and non-blocking Kotlin applications.