Basics of Kotlin Coroutine with examples

Raghunandan Kavi
2 min readFeb 4, 2024

Coroutines are alternative to threads in java. While i have interviwed freshers for android developers most of them have used coroutines in their android projects but fail to understand the basics.

  1. Lets say you have two coroutines and one of them throw’s a excpetion due to this the parent and all of childs job are cancelled. However there might be a requirement where you might still want to continue the other when one throws an exception. We can achieve this using supervisor scope

fun main() {
runBlocking {
val job = supervisorScope {
launch {
println("Launch 1")
throw Exception("Launch 1 throws exception")
println("Launch 1 done")
}
launch {
println("Launch 2")
delay(500)
println("Launch 2 done")
}
}
println("When launch 1 throws exception launch 2 is not cancelled")
}
}

To see the cancellation run the below example.

fun main() {
runBlocking {
launch {
println("Launch 1")
throw Exception("I am throwing exception")
}
launch {
println("Launch 2")
println("Launch 2 done")
}

println("Launch 2 is cancelled due to structured concurrency")
}
}

2. Blocking vs non Blocking

fun main() {
runBlocking {
launch {
println("Launch 1")
Thread.sleep(1000)
println("Launch 1 done")
}
launch {
println("Launch 2")
println("Launch 2 Done")
}

}
}

Thread.sleep in the above actually blocks the coroutines preventing the second coroutine to run. To prevent this we can use delay

fun main() {
runBlocking {
launch {
println("Launch 1")
Thread.sleep(1000)
println("Launch 1 done")
}
launch {
println("Launch 2")
println("Launch 2 Done")
}

}
}

The concept of suspend is to not block a coroutine but it’s state is maintained in a stack until some operation happens and we can resume with the result.

3. withContext is something similar. If you have two of them like below it runs one after another the withContext blocks

fun main() { 
runBlocking {
doSomething()
}
}

suspend fun doSomething() {
withContext(Dispatchers.IO) {
println("Context 1")
delay(2000)
println("Context 1 done")
}

withContext(Dispatchers.IO) {
println("Context 2")
delay(2000)
println("Context 2 done")
}
}

4. Lets say you have a shared counter variable and you want to increment it and print it until 100. Let say you have two coroutines. Each of them has access to this shared counter varaible and each of them does read and write. To prevent inconsistency we need to make it mutually exclusive with locks. This is done using with mutex as you can see in the below example.

fun main() = runBlocking {
// Shared counter variable
var counter = 0

// Mutex for synchronization for mutual exclusion.
val mutex = Mutex()

// Coroutine a
val coroutineA = launch {
while (counter <= 100) {
mutex.withLock {
println("Coroutine A: Incrementing counter to $counter")
counter++
}
delay(100) // Simulating some work, e.g., processing or waiting
}
}

// Coroutine b
val coroutineB = launch {
while (counter <= 100) {
mutex.withLock {
println("Coroutine B: Incrementing counter to $counter")
counter++
}
delay(100) // Simulating some work, e.g., processing or waiting
}
}
// Wait for both coroutines to complete
coroutineA.join()
coroutineB.join()
println("Counter reached 100!")
}

--

--