You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何在主线程调用runBlocking(Dispatchers.Main)总会死锁?

关于runBlocking在主线程中指定Dispatchers.Main导致阻塞的疑问

根据官方文档定义,runBlocking 的作用是:

Runs a new coroutine and blocks the current thread until its completion.
(翻译:启动一个新协程,并阻塞当前线程直到协程完成。)

我发现新协程仍可在当前线程中调度,但为何当当前线程为MAIN(即Dispatchers.Main)时,调用runBlocking(Dispatchers.Main)总会出现阻塞?

测试代码

无阻塞的情况

@Test
fun runInMainThreadButNotBlocked() {
    val handler = Handler(Looper.getMainLooper())
    handler.post {
        val mainTid = Thread.currentThread()
        Log.d("test", "main thread id is $mainTid")
        runBlocking {
            val tid = Thread.currentThread()
            Log.d("test", "runBlocking enter, tid is $tid") // 打印主线程
            Log.d("test", "runBlocking leave")
        }
    }
}

始终阻塞的情况

@Test
fun runInMainThreadAlwaysBlocked() {
    val handler = Handler(Looper.getMainLooper())
    handler.post {
        val mainTid = Thread.currentThread()
        Log.d("test", "main thread id is $mainTid")
        runBlocking(Dispatchers.Main) {  // 永远无法进入
            val tid = Thread.currentThread()
            Log.d("test", "runBlocking enter, tid is $tid") // 仍会打印主线程(如果能执行的话)
            Log.d("test", "runBlocking leave")
        }
    }
}

原因解释

  1. runBlocking的默认调度逻辑
    当不指定Dispatcher调用runBlocking时,它会使用当前线程专属的BlockingEventLoop作为调度器。对于主线程来说,这个EventLoop不需要依赖主线程的Looper消息队列,而是直接在当前线程上同步执行协程体,所以不会出现阻塞,协程代码能正常运行。

  2. 指定Dispatchers.Main时的死锁逻辑
    当显式传入Dispatchers.Main时,runBlocking会把协程任务提交到主线程的Looper消息队列中等待执行。但runBlocking本身会阻塞当前主线程,直到协程完成——而主线程被阻塞后,根本无法处理Looper消息队列里的协程任务,协程永远得不到执行,runBlocking也就永远不会结束,形成了死锁。

这就是第二个测试中协程体永远无法进入、程序一直阻塞的核心原因。

内容的提问来源于stack exchange,提问作者progquester

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 03:57:13