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

重复尝试获取锁能否有效防死锁?线程同步死锁优化方案咨询

线程同步与死锁预防:Data类swapValue方法的线程安全实现

问题背景

现有Data类持有一个长整型变量,多线程会调用其swapValue方法实现两个实例的值交换。原始代码如下:

public class Data {

    private long value;

    public Data(long value) {
        this.value = value;
    }
    public synchronized long getValue() {
        return value;
    }
    public synchronized void setValue(long value) {
        this.value = value;
    }

    public void swapValue(Data other) {
        long temp = getValue();
        long newValue = other.getValue();
        setValue(newValue);
        other.setValue(temp);
    }
}

swapValue方法需要保证线程安全,且资源不可用时不能跳过值交换。直接在方法签名加synchronized会导致死锁(比如线程A调用dataA.swapValue(dataB),线程B调用dataB.swapValue(dataA),双方互相持有锁等待对方释放)。

我自己实现了一个解决方案,依赖线程释放资源后另一线程能在资源未被锁定时获取它的概率,代码如下:

private Lock lock = new ReentrantLock();

...

public void swapValue(Data other) {
    lock.lock();
    while(!other.lock.tryLock())
    {
        lock.unlock();
        lock.lock();
    }

    long temp = getValue();
    long newValue = other.getValue();
    setValue(newValue);
    other.setValue(temp);
    
    other.lock.unlock();
    lock.unlock();

}

感觉这是个权宜之计,想请教:

  • 这类方案是否常用?
  • 是否存在行为更具确定性且实用的解决方案?

问题解答

你提出的方案是否常用?

这个方案本质是自旋+锁抢占的重试机制,属于乐观的死锁规避方式,但不属于工业级代码的常用方案:

  • 依赖概率,极端高并发场景下可能出现长时间循环重试,浪费CPU资源;
  • lock.unlock()和lock.lock()之间存在间隙,当前线程会完全失去自身锁的持有权,可能被其他线程抢占,导致交换逻辑执行时机不可控;
  • 可读性差,维护成本高,其他开发者很难快速理解代码意图。

更具确定性的实用解决方案

1. 固定锁的获取顺序

死锁的核心原因是交叉锁获取,只要保证所有线程获取锁的顺序一致,就能从根源避免死锁。可以基于对象的唯一标识(或哈希值)确定锁的获取顺序:

public class Data {
    private long value;
    private final Lock lock = new ReentrantLock();
    // 给每个Data实例分配唯一标识,避免哈希冲突
    private final long id = System.nanoTime();

    public Data(long value) {
        this.value = value;
    }

    public long getValue() {
        lock.lock();
        try {
            return value;
        } finally {
            lock.unlock();
        }
    }

    public void setValue(long value) {
        lock.lock();
        try {
            this.value = value;
        } finally {
            lock.unlock();
        }
    }

    public void swapValue(Data other) {
        // 固定先获取id更小的实例的锁
        Data first = this.id < other.id ? this : other;
        Data second = this.id >= other.id ? this : other;

        first.lock.lock();
        try {
            second.lock.lock();
            try {
                long temp = this.value;
                this.value = other.value;
                other.value = temp;
            } finally {
                second.lock.unlock();
            }
        } finally {
            first.lock.unlock();
        }
    }
}

这种方式完全消除死锁可能,行为确定,是死锁预防的常用方案之一。

2. 使用带超时的tryLock显式控制

如果无法固定锁顺序,可以给tryLock添加超时时间,避免无限等待,同时加入短暂休眠减少CPU空转:

public void swapValue(Data other) throws InterruptedException {
    boolean acquiredSelf = false;
    boolean acquiredOther = false;
    try {
        while (!acquiredSelf || !acquiredOther) {
            acquiredSelf = lock.tryLock(100, TimeUnit.MILLISECONDS);
            if (acquiredSelf) {
                acquiredOther = other.lock.tryLock(100, TimeUnit.MILLISECONDS);
                if (!acquiredOther) {
                    lock.unlock();
                    acquiredSelf = false;
                    // 短暂休眠避免CPU空转
                    Thread.sleep(50);
                }
            }
        }
        // 执行交换逻辑
        long temp = this.value;
        this.value = other.value;
        other.value = temp;
    } finally {
        if (acquiredOther) {
            other.lock.unlock();
        }
        if (acquiredSelf) {
            lock.unlock();
        }
    }
}

这种方式通过超时机制避免无限阻塞,行为可控,适合无法固定锁顺序的场景。

3. 引入全局锁(简单直接但性能较低)

如果交换操作并发量不高,可以引入全局锁,所有交换操作串行执行:

public class Data {
    private static final Lock GLOBAL_SWAP_LOCK = new ReentrantLock();
    private long value;
    private final Lock lock = new ReentrantLock();

    // 构造方法、getValue、setValue实现同前

    public void swapValue(Data other) {
        GLOBAL_SWAP_LOCK.lock();
        try {
            this.lock.lock();
            other.lock.lock();
            try {
                long temp = this.value;
                this.value = other.value;
                other.value = temp;
            } finally {
                other.lock.unlock();
                this.lock.unlock();
            }
        } finally {
            GLOBAL_SWAP_LOCK.unlock();
        }
    }
}

这种方式实现最简单,但会降低并发性能,适合低并发场景。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 07:15:33