runBlocking — o que é, ponte bloqueante e como funciona

Autor: IT Sectr Publicado: 2026-06-22 Tempo de leitura: 7 min

runBlocking — um Coroutine Builder em Kotlin que bloqueia a thread atual até que a corrotina passada seja concluída. Ao contrário de launch e async, não é uma função suspend e pode ser chamado de código normal (bloqueante). De acordo com a documentação JetBrains, 2024, runBlocking serve como uma ponte entre os mundos síncrono e assíncrono, permitindo iniciar corrotinas da função main e testes.

Principais conclusões

  • runBlocking — um builder bloqueante que cria um CoroutineScope e aguarda a conclusão da corrotina
  • Bloqueio de thread — runBlocking mantém a thread atual até a conclusão total da corrotina e todos os seus filhos
  • Pontos de entrada — main(), testes JUnit e ponte entre código bloqueante e assíncrono
  • Proibido na thread principal do Android — chamar runBlocking na thread de UI causa ANR
  • Alternativas — lifecycleScope, viewModelScope, TestCoroutineDispatcher para Android

O que é runBlocking?

runBlocking é uma função Kotlin que cria um novo CoroutineScope e executa a corrotina passada, bloqueando a thread atual até sua conclusão completa. Ao contrário de todos os outros Coroutine Builders, runBlocking não é uma função suspend e pode ser chamado de código síncrono comum. A assinatura de runBlocking recebe um CoroutineContext e um bloco suspend, retornando um resultado do tipo T.

kotlin
public fun <T> runBlocking(
    context: CoroutineContext = EmptyCoroutineContext,
    block: suspend CoroutineScope.() -> T
): T

runBlocking inicia um novo event-loop na thread atual. Quando a corrotina chama uma função suspend (por exemplo, delay() ou await()), runBlocking bloqueia a thread e executa outras corrotinas agendadas na mesma thread até que a suspensa seja retomada. Isso é bloqueio cooperativo — a thread não fica ociosa, mas processa outras corrotinas.

Como funciona o runBlocking

O mecanismo interno do runBlocking é baseado em um event-loop: ao chamar uma função suspend, o runBlocking pausa a execução do bloco atual e executa outras corrotinas da fila. Quando a função suspend é concluída, a execução é retomada. Este ciclo continua até que todas as corrotinas terminem.

Event-loop internamente

runBlocking usa seu próprio pool de thread única para executar corrotinas. Ao contrário de Dispatchers.IO ou Default, runBlocking não alterna threads — ele manipula todas as corrotinas na thread atual, intercalando sua execução. Este é o único builder que garante a execução na mesma thread.

kotlin
fun main() {
    val threadName = Thread.currentThread().getName()
    println("Antes de runBlocking em $threadName")

    val result = runBlocking {
        println("Dentro de runBlocking em ${Thread.currentThread().getName()}")
        delay(500L)
        "Done"
    }

    println("Depois de runBlocking: $result")
}

A saída mostrará que todas as três instruções println são executadas na mesma thread. runBlocking não alterna threads, mas organiza multitarefa cooperativa dentro de uma única thread usando um event-loop.

Quando usar runBlocking

runBlocking é justificado em três cenários: o ponto de entrada main() em aplicações de console, testes unitários de funções suspend e ponte — chamar código suspend de bibliotecas baseadas em callbacks ou bloqueantes. Em código Android de produção, usá-lo na thread principal é estritamente proibido.

CenárioAplicabilidadeRiscos
main() de aplicação de consoleSimNenhum — é o ponto de entrada, a thread não bloqueia a UI
Testes JUnitSimMínimos — testes são síncronos por definição
Thread UI do AndroidNãoANR, lentidão, congelamento da interface
Callback → CoroutineSim, com cautelaBloqueio do pool de threads em operações longas

Para testes Android, use kotlinx-coroutines-test com TestDispatcher em vez de runBlocking. Isso dá controle de tempo, limpeza automática e isolamento de testes.

Alternativas ao runBlocking

Na maioria dos cenários, runBlocking pode e deve ser substituído por alternativas assíncronas. Para Android, são viewModelScope, lifecycleScope ou CoroutineScope com o dispatcher adequado. Para testes — TestCoroutineDispatcher e runTest.

kotlin
    // Ruim: runBlocking na thread principal do Android
runBlocking(Dispatchers.Main) {
    val result = networkApi.fetchData()
    textView.setText(result)
}

// Bom: lifecycleScope
lifecycleScope.launch {
    val result = withContext(Dispatchers.IO) { networkApi.fetchData() }
    textView.setText(result)
}

Para testes, a substituição é runTest de kotlinx-coroutines-test. Ele cria um TestCoroutineScope com tempo virtual, permitindo testar atrasos sem espera real. Isso acelera os testes e os torna determinísticos.

Exemplos de uso do runBlocking

O cenário mais comum é testar funções suspend. runBlocking em testes permite aguardar sincronamente um resultado de corrotina sem alterar a arquitetura. O segundo cenário são bibliotecas com API de callbacks, onde funções suspend são chamadas de um contexto bloqueante via runBlocking.

kotlin
// Testar função suspend com runBlocking
class RepositoryTest {
    @Test
    fun `fetchUser returns correct data`() {
        val repository = UserRepository(FakeApi())

        val result = runBlocking {
            repository.fetchUser("123")
        }

        assertEquals("John", result.name)
        assertEquals("john@test.com", result.email)
    }
}

Para a ponte entre callbacks e o mundo suspend, use CompletableDeferred em combinação com runBlocking em vez de callbacks — isso simplifica cadeias de operações assíncronas e melhora a legibilidade do código.

Perigos do uso incorreto

O uso inadequado de runBlocking é um dos erros comuns ao migrar de uma abordagem bloqueante para corrotinas. Principais problemas: chamada na thread principal do Android, aninhamento de runBlocking, uso dentro de funções assíncronas e execução de operações longas com runBlocking.

  • ANR — runBlocking na thread principal do Android bloqueia a renderização da UI por mais de 5 segundos
  • Deadlock — runBlocking aninhado dentro de uma corrotina na mesma thread leva a um bloqueio mútuo
  • Confusão com dispatchers — Dispatchers.Main dentro de runBlocking em uma thread de background não tem Looper e lança exceção
  • Vazamentos de memória — runBlocking não é cancelado automaticamente ao destruir Activity/Fragment

Regra de ouro: runBlocking é uma ponte, não uma substituição. Use-o apenas para conectar mundos bloqueantes e não bloqueantes. Para todas as outras tarefas, use launch, async ou lifecycleScope.

Perguntas frequentes

Por que runBlocking bloqueia a thread enquanto outros builders não?

runBlocking é o único builder que não é uma função suspend. Ele inicia um event-loop na thread atual e não retorna o controle até que todas as corrotinas sejam concluídas. launch e async retornam o controle imediatamente, executando a corrotina em segundo plano.

Pode-se usar runBlocking no Android ViewModel?

Não recomendado. ViewModel tem um viewModelScope embutido que gerencia automaticamente as corrotinas e as cancela quando destruído. runBlocking no ViewModel bloqueia a thread e não responde ao cancelamento do ciclo de vida.

O que usar em vez de runBlocking em testes unitários?

Use runTest da biblioteca kotlinx-coroutines-test. Ele fornece um TestCoroutineScope com controle de tempo virtual, cancelamento automático e execução determinística.

O que é event-loop no runBlocking?

Event-loop é um ciclo de processamento de eventos dentro do runBlocking. Quando uma corrotina é suspensa (ex. delay()), o event-loop alterna para executar outras corrotinas prontas na mesma thread. Isso cria a ilusão de multitarefa sem alternância de thread.

O que acontece ao chamar runBlocking dentro de runBlocking?

runBlocking aninhado na mesma thread cria um deadlock — o bloco externo aguarda o interno, mas o interno não pode começar até que o externo termine. Em threads diferentes é permitido, mas fortemente desaconselhado devido à complexidade de depuração.

Resumo

  • runBlocking — um Coroutine Builder bloqueante, ponte entre código bloqueante e assíncrono
  • Event-loop runBlocking processa corrotinas cooperativamente em uma única thread sem alternância
  • Cenários permitidos — main(), testes JUnit, ponte de bibliotecas de callbacks
  • Cenários proibidos — thread UI do Android, chamadas aninhadas, operações longas
  • Alternativas — lifecycleScope, viewModelScope, runTest para testes
  • Risco de ANR — runBlocking na thread principal congela o app após 5 segundos
  • Para código Android de produção, use builders assíncronos — runBlocking não foi projetado para UI

Vamos desenvolver um aplicativo móvel chave na mão

A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.

Discutir o projeto

Leia também