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

持有多把锁时为何不应调用wait方法?

为什么持有多把锁时不应调用wait方法?

先来看你提供的这段代码,它确实存在同时持有两把锁的情况:

public synchronized void methodOne(List<String> profileNames, ParameterAttributes parameterAttributes) throws InterruptedException {
    if (profileNames != null && !profileNames.isEmpty()) {
        profileNames.forEach(profileName -> System.out.println(profileName));
    }
    synchronized(lockObj) {
        lockObj.wait();
    }
}

这里的synchronized方法会先获取当前类实例的锁,之后又在内部代码块获取了lockObj的锁,接着调用了lockObj.wait()。这种操作会带来几个严重的问题:

  • 不必要的长期阻塞,引发性能或死锁风险
    调用lockObj.wait()时,当前线程只会释放lockObj这一把锁,但不会释放当前实例的锁!这就意味着,所有想要访问该类其他synchronized方法(或者同步当前实例的代码块)的线程,都会被持续阻塞,直到这个线程被唤醒并完全执行完methodOne方法。如果wait()的唤醒条件迟迟不满足(比如其他线程忘记调用notify/notifyAll,或者业务上需要等待很长时间),当前实例的锁会被长时间霸占,直接拖垮程序的并发能力,甚至可能引发连锁阻塞,间接导致死锁。

  • 锁逻辑混乱,降低代码可维护性
    持有多锁时调用wait,会让锁的释放逻辑变得非常不直观。后续维护代码的开发者很难第一时间意识到:这个方法同时拿了两把锁,而wait只释放了其中一把。这种隐藏的逻辑很容易引发后续bug,比如新增同步逻辑时没考虑锁的持有情况,进一步加剧并发问题。

  • 违背并发编程的最小持有原则
    并发编程里有个核心原则:只在必要的时间段持有必要的锁。你这段代码里,遍历profileNames的逻辑(如果确实需要锁的话)只需要当前实例的锁,而wait逻辑只需要lockObj的锁。把两个逻辑放在同一个持有多锁的方法里,本身就扩大了锁的持有范围,再加上wait导致的锁不释放,完全违背了最小持有原则,会大幅提升并发冲突的概率。

举个实际的例子:如果这个类还有另一个synchronized方法methodTwo(),当线程A在methodOne里进入wait状态后,线程B想调用methodTwo()就会被一直卡住——哪怕线程B根本不需要操作lockObj,这完全是毫无意义的阻塞。

正确的做法应该是拆分锁的持有范围,让每个同步块只持有完成对应逻辑所需的锁,调用wait时确保只持有当前wait对象的那一把锁,避免其他锁被无端霸占。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 10:08:03