当前位置: 首页 > 知识库问答 >
问题:

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

薛焱
2023-03-14

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

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

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

getBalance()方法在确保内存可见性方面做得对吗?

在getBalance()中,try中的“return”看起来很难看,但是必须在锁仍然存在时读取balance字段吗?

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();
    }
}

共有1个答案

史鹏云
2023-03-14

您的锁顺序看起来很好,死锁预防机制也很好,但有两个建议是关于subtractFromBalanceaddToBalance以确保正确性(您的代码可能已经这样做了)

>

  • subtratFromBalanceaddToBalance中进行验证,例如,如果大于subtratFromBalance的当前余额,则抛出一个IllegalArgumentExctive或其他任何值(我假设不允许负余额)。您可能会在之前进行此验证,但您也需要在获得锁后进行验证。

    subtractFromBalanceaddToBalance中的写锁上调用isHeldByCurrentThread,如果锁未保持,则引发异常。

    如果您能够承受数据中的暂时不一致,那么您可以完全消除读锁定:使用原子参考

  •  类似资料:
    • 读者: 这看起来像是一个僵局,但有几件事让我怀疑: 我找不到另一个可能持有相同锁的线程 4秒后进行线程转储会得到相同的结果,但现在所有线程都报告,这与第一次转储中的不同。

    • 什么事件名称谷歌地图触摸初始化标记,出现标题或描述?google.maps.event.add监听器(标记,触摸启动,

    • 问题内容: 如果我想跨多个页面(如菜单)使用一个通用的UI,推荐的这样做方法是什么? 它包含模板代码和后端控制器(类似于LiftWeb框架中的“片段”)。 我知道有一个Play菜单模块,但我对总体上如何实现更感兴趣。 问题答案: 有两种方法可以将通用视图代码包含在Play框架中。 您可以使用标签或标签。 顾名思义,extends标签是从父视图扩展的。当您创建新的应用程序时,默认情况下,扩展标记用于

    • 问题内容: 和 第二个代码产生了一个空指针异常,该怎么做才能使下一个等效? 问题答案: 我可以看到,如果players某个自定义java.lang.Iterable的get()实现的实现被破坏,或者至少以一种异常的方式(与的行为不同),就会发生这种情况。 除此之外,我唯一能想到的就是您未在代码中向我们展示的某些内容导致了某些错误。 如果执行此操作会怎样?

    • 给出的代码打印1 0 12然后显示分段错误,尝试了很长时间来解决这个问题,但没有发现错误,请帮助:我没有看到分段错误的原因,一直在尝试用链表实现队列,尝试使用不同的编译器,但没有帮助。在给定的代码中,如果可能的话,请找出错误,并告诉我如何改进这段代码