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

Spring Web服务器异步调用共享资源同步失效问题排查

问题分析:SpringBoot中同步机制混用导致锁日志异常

咱们先梳理下你遇到的情况:你有个SpringBoot Web服务,里面有个操作单例状态的路由,为了保证多调用方访问时的线程安全,你给set()方法加了Kotlin的@Synchronized注解,同时之前还尝试过用ReentrantLock,相关代码还没移除。但现在日志里出现了多次连续的“锁定”日志,之后才批量出现“释放”日志,和你期望的“锁定-释放”交替出现的情况不符。

你的核心代码

@Synchronized public fun <T> set(t: T) { 
    val incomingState = State.from(t) 
    if(incomingState == innerState){ return } 
    change(incomingState) 
} 

@Synchronized private fun change(newState: State){ 
    this.stateAccessLock.lock() 
    SystemLog.logResourceLocking("HomeState", isLocked = true) 
    SystemLog.logHomeEvent("Changing HomeState from '${innerState.name}' to '${newState.name}'") 
    try { 
        Home.INSTANCE.EXTENSION.broadcastStateChange( innerState?.name, newState.name ) 
    } finally { 
        innerState = newState 
        Home.save() 
        stateAccessLock.unlock() 
        SystemLog.logResourceLocking("HomeState", isLocked = false) 
    } 
}

// ReentrantLock相关代码
private var stateAccessLock = ReentrantLock() 
@Autowired fun HomeState(@Qualifier("stateAccessLock") stateAccessLock: ReentrantLock) { 
    this.stateAccessLock = stateAccessLock 
}

日志异常情况

实际日志(不符合预期):

[2020-10-09T16:07:01.709] LOCK: Resource locked
[2020-10-09T16:07:01.710] LOCK: Resource locked
[2020-10-09T16:07:01.725] LOCK: Resource locked
[2020-10-09T16:07:01.727] LOCK: Resource locked
[2020-10-09T16:07:01.818] LOCK: Resource unlocked
[2020-10-09T16:07:01.828] LOCK: Resource unlocked
[2020-10-09T16:07:01.829] LOCK: Resource unlocked
[2020-10-09T16:07:01.839] LOCK: Resource unlocked

期望日志:

[2020-10-09T16:07:01.709] LOCK: Resource locked
[2020-10-09T16:07:01.710] LOCK: Resource unlocked
[2020-10-09T16:07:01.725] LOCK: Resource locked
[2020-10-09T16:07:01.727] LOCK: Resource unlocked
...


问题根源:同步机制混用 + 锁实例不唯一

你忽略了两个关键问题:

  1. 同时混用两种独立的同步机制
    Kotlin的@Synchronized注解本质是给方法加了对象级别的内置锁(和Java的synchronized方法一致),而你又在change()里手动加了ReentrantLock的独占锁。这两种锁是完全独立的,互相不会感知对方的状态。

    • 如果HomeState是多实例的,每个实例的@Synchronized锁是各自独立的,多个线程可以同时进入不同实例的set()方法,然后各自去抢自己的ReentrantLock(如果锁实例不唯一的话),导致多个线程同时成功锁定,日志连续输出“locked”。
  2. ReentrantLock实例可能不是单例
    从你的注入代码来看,如果Spring容器中stateAccessLock这个bean没有被定义为单例(比如误设为原型scope),那么每个HomeState实例都会拿到一个独立的ReentrantLock对象。这种情况下,多个线程操作不同HomeState实例时,各自的锁互不干扰,自然能同时锁定,产生连续的锁定日志。


解决方案:二选一,统一同步机制

你只需要选择一种同步机制即可,不需要同时使用:

方案1:只用@Synchronized注解(移除ReentrantLock)

既然@Synchronized已经能保证同一时间只有一个线程进入set()和change()方法(只要HomeState是单例),就不需要额外的ReentrantLock了。修改后代码如下:

@Synchronized public fun <T> set(t: T) { 
    val incomingState = State.from(t) 
    if(incomingState == innerState){ return } 
    change(incomingState) 
} 

@Synchronized private fun change(newState: State){ 
    SystemLog.logResourceLocking("HomeState", isLocked = true) 
    SystemLog.logHomeEvent("Changing HomeState from '${innerState.name}' to '${newState.name}'") 
    try { 
        Home.INSTANCE.EXTENSION.broadcastStateChange( innerState?.name, newState.name ) 
    } finally { 
        innerState = newState 
        Home.save() 
        SystemLog.logResourceLocking("HomeState", isLocked = false) 
    } 
}

方案2:只用ReentrantLock(移除@Synchronized)

确保stateAccessLock是单例bean,然后在set()方法中统一加锁,避免重复加锁:

// 确保stateAccessLock是单例bean
@Bean("stateAccessLock")
fun stateAccessLock(): ReentrantLock {
    return ReentrantLock()
}

// HomeState类代码
private lateinit var stateAccessLock: ReentrantLock

@Autowired fun HomeState(@Qualifier("stateAccessLock") stateAccessLock: ReentrantLock) { 
    this.stateAccessLock = stateAccessLock 
}

public fun <T> set(t: T) { 
    stateAccessLock.lock()
    try {
        val incomingState = State.from(t) 
        if(incomingState == innerState){ return } 
        change(incomingState) 
    } finally {
        stateAccessLock.unlock()
    }
} 

private fun change(newState: State){ 
    SystemLog.logResourceLocking("HomeState", isLocked = true) 
    SystemLog.logHomeEvent("Changing HomeState from '${innerState.name}' to '${newState.name}'") 
    try { 
        Home.INSTANCE.EXTENSION.broadcastStateChange( innerState?.name, newState.name ) 
    } finally { 
        innerState = newState 
        Home.save() 
        SystemLog.logResourceLocking("HomeState", isLocked = false) 
    } 
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 13:47:29