SyntaxStudy
Sign Up
Kotlin Coroutine Basics with launch and runBlocking
Kotlin Beginner 1 min read

Coroutine Basics with launch and runBlocking

Coroutines are Kotlin's solution for asynchronous and concurrent programming. Unlike threads, coroutines are lightweight: you can run thousands of them on a handful of threads because a suspended coroutine releases its thread to other work rather than blocking it. The launch coroutine builder starts a new coroutine and returns a Job that you can use to wait for it or cancel it. launch is fire-and-forget — it does not return a result. runBlocking bridges the blocking world and the coroutine world by blocking the current thread until all coroutines inside it complete, making it useful for main functions and tests. A coroutine is always started in a CoroutineScope. The scope defines the lifetime of its children: when the scope is cancelled all its coroutines are cancelled recursively. This structured concurrency model prevents coroutine leaks.
Example
import kotlinx.coroutines.*

fun main() = runBlocking {
    println("Main starts on ${Thread.currentThread().name}")

    // launch returns a Job
    val job1 = launch {
        delay(200)
        println("Job 1 done on ${Thread.currentThread().name}")
    }

    val job2 = launch {
        delay(100)
        println("Job 2 done on ${Thread.currentThread().name}")
    }

    println("Waiting for jobs...")
    job1.join()   // wait for job1 to complete
    // job2 likely already done

    // Launch many coroutines — lightweight vs threads
    val jobs = (1..50_000).map { i ->
        launch {
            delay(1000)
            print(".")
        }
    }
    jobs.forEach { it.join() }
    println()
    println("All 50,000 coroutines done!")
}

// coroutineScope — structured concurrency in a suspend function
suspend fun doWork() = coroutineScope {
    val a = launch { delay(100); println("A") }
    val b = launch { delay(50);  println("B") }
    // Both complete before doWork returns
}