我熟悉在一个帐户和另一个帐户之间转账时使用同步的并发示例,例如,两个帐户的锁定是根据帐号排序的,这样就不会发生死锁。
我想探索使用 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();
}
}
最佳答案
您的锁顺序和死锁预防机制看起来都不错,但有两个建议 subtractFromBalance
和addToBalance
确保正确性(并且您的代码可能已经这样做了)
在
subtractFromBalance
内进行验证和addToBalance
,即抛出IllegalArgumentException
或者无论如何amount
大于subtractFromBalance
的当前余额(我假设不允许出现负余额)。您可能会提前进行此验证,但您还需要在获取锁后进行此验证。调用
isHeldByCurrentThread
subtractFromBalance
内的写锁和addToBalance
如果没有持有锁,则抛出异常。
如果您能够承受数据中的一些临时不一致,那么您可以完全消除读锁定:使用 AtomicReference<BigDecimal>
余额( getBalance()
简单地变成 return balance.get()
)。不一致之处在于,如果您从 AccountA
中减去钱,并将其添加到 AccountB
然后同时调用getBalance
可能会返回 AccountA
的新余额旧余额为 AccountB
,但是这在您的系统中可能是安全的,因为它只会低估可用资金,并且帐户最终会保持一致。
关于java - 这段代码是否正确使用了 ReentrantReadWriteLock?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27590181/