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

Java双线程操作不同BCell对象执行swap方法可能引发什么问题?

分析BCell类swap方法的并发死锁问题

兄弟,你这个场景妥妥会触发死锁!我给你一步步拆解下发生的过程,你立马就能明白。

先把你的代码贴出来方便分析(顺便补了你漏写的getValue()括号):

class BCell { 
    int value; 
    public synchronized int getValue() { return value; } 
    public synchronized void setValue(int i) { value=i; } 
    public synchronized void swap(BCell x) { 
        int temp = getValue(); 
        setValue(x.getValue()); 
        x.setValue(temp); 
    } 
}

死锁发生的具体过程

当thread1执行p1.swap(p2)时:

  1. 因为swap是synchronized方法,thread1会先获取p1对象的锁。
  2. 接着执行getValue()——这个方法也是synchronized,但因为已经持有p1的锁,所以能顺利拿到当前value。
  3. 下一步调用x.getValue()(也就是p2.getValue()),这时候thread1需要去获取p2对象的锁。

与此同时,thread2在执行p2.swap(p1):

  1. 同样,thread2先获取p2对象的锁。
  2. 执行到x.getValue()(也就是p1.getValue()),这时候它需要去获取p1对象的锁。

现在关键矛盾来了:

  • thread1拿着p1的锁,死死等着拿p2的锁;
  • thread2拿着p2的锁,死死等着拿p1的锁。

两个线程都不肯释放自己手里的锁,也拿不到需要的锁,就这么无限卡住了——这就是典型的死锁,完全符合死锁的四大条件:互斥、占有且等待、不可抢占、循环等待。

怎么解决?

核心思路是:让所有线程在获取多个对象锁时,都遵循完全一致的顺序,这样就不会出现循环等待的情况。

比如我们可以根据对象的哈希值来确定锁的顺序,不管是p1.swap(p2)还是p2.swap(p1),都会先锁哈希值更小的那个对象,再锁更大的那个。修改后的swap方法如下:

public void swap(BCell x) {
    // 确定锁的顺序,避免循环等待
    BCell first = this.hashCode() < x.hashCode() ? this : x;
    BCell second = this.hashCode() > x.hashCode() ? this : x;
    
    // 按顺序获取锁
    synchronized (first) {
        synchronized (second) {
            int temp = getValue();
            setValue(x.getValue());
            x.setValue(temp);
        }
    }
}

这里要提一句:如果两个对象的哈希值恰好相同(概率极低),还是可能有问题。这种极端情况可以额外加一个全局锁兜底,不过绝大多数业务场景下,哈希值排序的方式已经足够解决问题了。

另外,也可以用Java的Lock接口配合tryLock方法来尝试获取锁,要是获取失败就释放已持有的锁,不过实现起来会比顺序锁复杂一些,顺序锁是最直接的解决方案。

内容的提问来源于stack exchange,提问作者Ioannis Brant-Ioannidis

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 06:36:04