What is runBlocking in Kotlin coroutines, and when should you use it?
answer
- Bridge: blocking world -> suspend world
- Blocks the current thread until done
- main() and tests only
- Returns the lambda's value
- Never inside another coroutine
basics
~10 srunBlocking is a bridge that lets regular blocking code call suspend functions. It blocks the current thread until the coroutines inside finish. Use it in main() and tests, not inside other coroutines.
solid answer
~40 srunBlocking is a coroutine builder that bridges the blocking world to the suspending world. It starts a new coroutine on the current thread and blocks that thread until all work inside its block (and child coroutines) completes, then returns the block's result. Unlike launch/async, it is a regular (non-suspend) function, so you can call it from ordinary code. Its typical uses are entry points that aren't suspend: the main() function of a small program/CLI and unit tests (though tests usually prefer runTest). Inside its lambda you have a CoroutineScope and can call suspend functions and other builders. You should NOT call runBlocking from inside an existing coroutine or suspend function, because it blocks the underlying thread and can stall a dispatcher's thread pool, defeating the point of coroutines.
code
kotlin · 11 linesimport kotlinx.coroutines.*
fun main() = runBlocking { // expression-body main, common idiom
val greeting = fetchGreeting()
println(greeting)
}
suspend fun fetchGreeting(): String {
delay(200) // simulate suspending work
return "hello"
}go deeper
Knows runBlocking bridges blocking to suspend code and blocks the thread until done; uses it in main/tests.
Explains it returns the block's value, propagates exceptions, and that runTest is the preferred test tool.
Articulates thread-pool starvation risks when nesting it, and contrasts it with withContext/coroutineScope.
Frames runBlocking as a controlled interop boundary policy across a codebase and bans it from hot/coroutine paths via lint/conventions.
## The problem runBlocking solves Kotlin coroutines split code into two worlds. **Suspend functions** (declared with the `suspend` keyword) can pause and resume without blocking a thread, but they can only be called from other suspend functions or from inside a coroutine. **Ordinary functions** (like `main()` before Kotlin made it suspend-capable, or a JUnit test method) cannot call suspend functions directly. `runBlocking` is the **bridge** between these two worlds. ## What it does ```kotlin import kotlinx.coroutines.* fun main() { val result = runBlocking { // this: CoroutineScope delay(100) // suspend function, legal here compute() // another suspend function } println(result) } suspend fun compute(): Int { delay(50); return 42 } ``` - It **creates a new coroutine** and runs the lambda you pass. - It **blocks the current thread** until that coroutine — and every child coroutine launched inside it — completes. - It **returns the value** the lambda produces (here the `Int` from `compute()`), or rethrows any exception thrown inside. - The lambda's receiver is a `CoroutineScope`, so inside you can use `delay`, `launch`, `async`, `withContext`, etc. ## When to use it - **`main()` of small apps / CLI tools** that need to run suspend code but aren't structured around a framework that already provides a scope. - **Tests**, to drive suspend code from a non-suspend test method. Modern code prefers `runTest` from `kotlinx-coroutines-test` (it skips `delay` virtually and gives better control), but `runBlocking` still works. - **Legacy / interop boundaries**: a blocking API (e.g. a servlet, a callback) that must call into suspend code. ## When NOT to use it - **Never inside another coroutine or a suspend function.** `runBlocking` blocks the carrier thread; if that thread belongs to a limited dispatcher pool (like `Dispatchers.Default`), you waste a pooled thread and risk thread-pool starvation/deadlock. The right tools there are `withContext`, `coroutineScope`, `async`/`await`. - **Not on the Android/UI main thread** in production — it freezes the UI. Use a lifecycle scope and `launch` instead. ## Key APIs and keywords - `suspend` — marks a function that can pause. - `runBlocking { ... }` — the bridge builder (returns the block's value). - `CoroutineScope` — the receiver type of the block. - `delay`, `launch`, `async`, `withContext` — used inside the block. - `runTest` — the test-specific replacement.
- Why is runBlocking discouraged inside an existing coroutine?It blocks the carrier thread instead of suspending, wasting a pooled dispatcher thread and risking starvation or deadlock; use withContext/coroutineScope instead.
- What should you usually use instead of runBlocking in tests?runTest from kotlinx-coroutines-test, which skips delays virtually and provides a TestScope and scheduler.
It's like a turnstile between the suspending highway and the blocking street: one person waits at the gate (the thread blocks) until everyone has passed through.
saying these in an interview costs you the question
- Thinking runBlocking is non-blocking like launch/async
- Using runBlocking inside a suspend function or another coroutine
- Believing runBlocking returns immediately while work continues in background
- Calling runBlocking on the Android UI thread in production
- Not knowing it returns the lambda's result value