Concurrency and Multithreading in Kotlin: A Complete Guide

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction to Concurrency and Multithreading in Kotlin



Modern applications increasingly require executing multiple tasks simultaneously — from processing network requests to working with databases. In the Kotlin language, which is actively used for Android development, server applications (Ktor, Spring) and even desktop solutions, powerful tools for working with concurrency and multithreading are implemented.



In this article, we will break down key concepts: traditional threads (Threads), coroutines (Coroutines) — a modern approach to asynchrony in Kotlin, as well as synchronization mechanisms. You will learn when to use threads and when to use coroutines, and get ready-made code examples.



1. Traditional Threads in Kotlin



Kotlin is fully compatible with Java, so you can use the standard Thread and Runnable classes. However, Kotlin adds syntactic sugar, making the code more concise.



Creating and starting a thread



The basic way to create a thread is to pass a lambda to the Thread constructor:



fun main() {    val thread = Thread {        println("Thread started: ${Thread.currentThread().name}")        for (i in 1..5) {            println("Count: $i")            Thread.sleep(500)        }    }    thread.start()    println("Main thread continues working")    thread.join() // wait for the thread to finish    println("Main thread finished")}


In this example, we created a thread that prints numbers from 1 to 5 with a 500 ms delay. The join() method forces the main thread to wait for the child thread to complete.



Problems with threads



  • High resource consumption — each thread requires a separate memory stack (usually 1 MB).
  • Complex synchronization — when working with shared data, it is easy to cause a race condition.
  • Limited quantity — you cannot run tens of thousands of threads on one device without losing performance.


This is why coroutines appeared in Kotlin — a lighter alternative.



2. Coroutines — Modern Concurrency in Kotlin



Coroutines are lightweight threads that can suspend their execution without blocking the system thread. They are the main tool for asynchronous programming in Kotlin.



Adding coroutines dependency



To work with coroutines, you need to add a dependency in build.gradle.kts:



dependencies {    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0")}


Basic coroutine example



import kotlinx.coroutines.*

fun main() = runBlocking { println("Start working in thread: ${Thread.currentThread().name}")

launch { // launch a new coroutine delay(1000) // non-blocking delay println("Coroutine: ${Thread.currentThread().name}") }

println("Main coroutine continues working") delay(2000) // wait for the child coroutine to finish println("End")}


Key points:

  • runBlocking — a bridge between regular code and coroutines (blocks the current thread).
  • launch — launches a coroutine that does not return a result.
  • delay — non-blocking delay, unlike Thread.sleep().


Async and await for getting results



If a coroutine should return a value, use async:



fun main() = runBlocking {    val deferred1 = async {        delay(1000)        "Result 1"    }    val deferred2 = async {        delay(500)        "Result 2"    }

println("Waiting for results...") val result1 = deferred1.await() val result2

Blogs

Book Recommendations