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

含suspend与非suspend函数的API如何实现线程安全?

要解决这个问题,核心是不能混用Java内置锁(@Synchronized)和协程Mutex——因为@Synchronized绑定线程,协程挂起时不会释放线程锁,会导致其他调用bar()的线程被无意义阻塞,甚至引发死锁。正确的做法是统一用Kotlin协程的Mutex作为同步机制,同时兼容非suspend的bar()函数。

具体改造步骤:

  1. 移除原来的@Synchronized注解,改用协程Mutex实例作为全局同步锁。
  2. 把原runWithoutConcurrency()里的临界区逻辑抽成一个无同步注解的私有函数(比如criticalSection())。
  3. 将foo()改为suspend函数,用mutex.withLock {}包裹临界区调用——这是协程安全的同步方式,挂起时会自动释放锁。
  4. 对于非suspend的bar(),用runBlocking {}包裹mutex.withLock {},把协程同步逻辑转换成普通线程可调用的形式,行为和原来的@Synchronized一致,同时和foo()共享同一锁保证互斥。

改造后的完整代码:

@AnyThread
class MyClass {
    // 全局协程互斥锁,统一同步所有临界区访问
    private val mutex = Mutex()

    suspend fun foo(): Int {
        mutex.withLock {
            criticalSection()
        }
        // .. 原foo()的剩余逻辑
        return 0 // 示例返回值
    }

    @AnyThread
    fun bar(): Int {
        // 用runBlocking将协程同步逻辑适配为非suspend调用
        runBlocking {
            mutex.withLock {
                criticalSection()
            }
        }
        // .. 原bar()的剩余逻辑
        return 0 // 示例返回值
    }

    // 抽离的临界区逻辑,无任何同步注解
    private fun criticalSection() { /* 原runWithoutConcurrency()里的代码 */ }
}

关键说明:

  • Mutex是协程感知的同步工具,不管是suspend函数还是通过runBlocking调用的普通函数,都能保证同一时间只有一个执行流进入临界区。
  • runBlocking在这里仅用于适配非suspend场景,不会引入额外性能问题——它只会阻塞当前线程直到临界区执行完毕,和原来@Synchronized的阻塞行为完全一致。
  • 必须统一用Mutex,不能让bar()继续用@Synchronized:两者锁的粒度不同(一个是协程锁,一个是线程锁),会导致临界区被同时访问,彻底破坏线程安全。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 13:35:02