java - 这段代码是否正确使用了 ReentrantReadWriteLock?

标签 java concurrency

我熟悉在一个帐户和另一个帐户之间转账时使用同步的并发示例,例如,两个帐户的锁定是根据帐号排序的,这样就不会发生死锁。

我想探索使用 ReentrantReadWriteLock,因为在我看来,这将允许 Account 对象的客户端进行并发读取,前提是没有客户端更新该对象。

我已经编写了代码并对其进行了测试,它似乎可以工作,但看起来有点难看,例如,Account 类公开其锁对象看起来有点奇怪,但似乎必须这样做?还想知道是否有任何我没有发现的潜在错误。

getBalance() 方法是否做了正确的事情来确保内存可见性?

在 getBalance() 中,try 中的“返回”看起来很丑陋,但是必须在锁仍然存在的情况下读取余额字段?

private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

public void writeLock(){
    lock.writeLock().lock();
}
public void readLock(){
    lock.readLock().lock();
}
public void writeUnlock(){
    lock.writeLock().unlock();
}
public void readUnlock(){
    lock.readLock().unlock();
}

public void transferToSafe(Account b, BigDecimal amount){

    Account firstAccountToLock=null;
    Account secondAccountToLock=null;

    // Let the smaller account always get the first lock
    if (this.getAccountNo() < b.getAccountNo()){
        firstAccountToLock = this;
        secondAccountToLock = b;
    }
    else {
        firstAccountToLock = b;
        secondAccountToLock = this;
    }
    try {
        firstAccountToLock.writeLock();

        try {
            secondAccountToLock.writeLock();

            this.subtractFromBalance(amount);
            b.addToBalance(amount);

        }
        finally {
            secondAccountToLock.writeUnlock();
        }
    }
    finally {
        firstAccountToLock.writeUnlock();
    }
}

public BigDecimal getBalance(){
    try {
        this.readLock();
        return balance;
    }
    finally {
        this.readUnlock();
    }
}

最佳答案

您的锁顺序和死锁预防机制看起来都不错,但有两个建议 subtractFromBalanceaddToBalance确保正确性(并且您的代码可能已经这样做了)

  1. subtractFromBalance内进行验证和addToBalance ,即抛出 IllegalArgumentException或者无论如何 amount大于 subtractFromBalance 的当前余额(我假设不允许出现负余额)。您可能会提前进行此验证,但您还需要在获取锁后进行此验证。

  2. 调用 isHeldByCurrentThread subtractFromBalance 内的写锁和addToBalance如果没有持有锁,则抛出异常。

如果您能够承受数据中的一些临时不一致,那么您可以完全消除读锁定:使用 AtomicReference<BigDecimal>余额( getBalance() 简单地变成 return balance.get() )。不一致之处在于,如果您从 AccountA 中减去钱,并将其添加到 AccountB然后同时调用getBalance可能会返回 AccountA 的新余额旧余额为 AccountB ,但是这在您的系统中可能是安全的,因为它只会低估可用资金,并且帐户最终会保持一致。

关于java - 这段代码是否正确使用了 ReentrantReadWriteLock?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27590181/

相关文章:

java - LibGdx 物理体编辑器

java - 使用签名小程序中的本地 Jar

Java Swing 应用程序 : how to get data from the GUI thread to another thread?

java - 在 Java 中将十进制度或度分秒转换为米

java - 如何在Java中解析远程计算机名称以获取其IP地址?

java - 寻找与 Java 的 Map 和 List 等效的 PHP?

.net - SwitchToThread/Thread.Yield 与 Thread.Sleep(0) 与 Thread.Sleep(1)

python子类化multiprocessing.Process

java - 消费者/生产者问题 : pause production on slow consumption

java - 如何创建一个弹出忙碌标签并且在执行时可以取消的任务?