我做了一个支持存款、取款、支票余额查询和转账的线程安全实现课程。我有一个班级帐户:
public class Account {
private int balance;
private String address;
private String userId;
final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
Lock transferLock = new ReentrantLock();
public Account(String userId){
this.userId = userId;
}
public void add(int amount){
balance += amount;
}
public void sub(int amount){
this.balance -= amount;
}
public int getBalance(){
return balance;
}
}我使用一个ReentrantReadWriteLock来存放和提取,这获得了一个写锁。读取锁是在检查帐户余额时获得的。A 'transferLock‘用于锁定从帐户A转到帐户B的资金。
以下是主要功能:
public boolean deposit(String userId, int amount){
Account account = getUserAccount(userId);
Lock accountWriteLock = account.lock.writeLock();
accountWriteLock.lock();
account.add(amount);
accountWriteLock.unlock();
return true;
}
public boolean withdraw(String userId, int amount){
Account account = getUserAccount(userId);
Lock accountWriteLock = account.lock.writeLock();
accountWriteLock.lock();
if(account.getBalance() < amount){
accountWriteLock.unlock();
return false;
}
account.sub(amount);
accountWriteLock.unlock();
return true;
}
public int checkBalance(String userId){
Account account = getUserAccount(userId);
Lock accountReadLock = account.lock.readLock();
accountReadLock.lock();
int balance = account.getBalance();
accountReadLock.unlock();
return balance;
}
public boolean transfer(String fromAccountUserId, int amount, String toAccountUserId) throws Exception {
Account fromAccount = getUserAccount(fromAccountUserId);
Account toAccount = getUserAccount(toAccountUserId);
Random rand = new Random();
Lock fromAccountTransferLock = fromAccount.transferLock;
Lock toAccountTransferLock = toAccount.transferLock;
while (true) {
if (fromAccountTransferLock.tryLock()) {
fromAccount.lock.writeLock();
if (toAccountTransferLock.tryLock()) {
try {
withdraw(fromAccountUserId, amount);
deposit(toAccountUserId, amount);
break;
} catch (Exception e) {
return false;
} finally {
toAccountTransferLock.unlock();
}
}
fromAccountTransferLock.unlock();
Thread.sleep(rand.nextInt(1001));
}
}
return true;
}假设getUserAccount(String userId)检索适当的帐户对象。
我想回顾的是
备注
编辑:下面是getUserAccount()代码:
public Account getUserAccount(String userId){
return new Account(userId);
}发布于 2020-07-13 09:22:20
让我们把这个拆开。
这通常是不好的做法,而且它没有意义,因为第二个锁(TransferLock)只在单个操作中使用。它不会保护其他行动。
如果您有一个静态只读锁用于传输,则可以使用以下语义: add/sub/get是全局读锁,传输是全局写锁,那么yu应该在常规操作上获得读锁,在传输op上获得写锁,但这将允许每次进行on;y单个传输,这肯定不是您想要实现的。
这可能导致外部的不受控制的使用,说未来版本的代码可能会误用锁。我可以看到,它们具有包级可见性,这是半好的,但是Account#add、Accound#sub、Account#getBallance是公共的,不受锁的保护,因此破坏了拥有包私有的目的。
我建议将锁的所有用法放在上面提到的实例方法中,并添加另一个方法:
class Account{
public void transfer(Account other, int amount) { ... }
}那么,所有与锁定相关的逻辑都将位于一个单一的受控位置。
您正在尝试使用tryLock和then循环来实现这一点,如果您已经使用了rw lock,这实际上是可行的,但是当您获得其他锁时,可能会出现死锁。在旋转循环中等待并不是真正的表演,你可以永远等待。
我建议用户使用您的rw锁,但首先,通过accountId对两个帐户进行排序,然后按照帐户ids从低到高的顺序使用写锁。
//pseudocode
if (acc1.id<acc2.id) {
acc1.lock.writeLock().lock()
acc2.lock.writeLock().lock()
}else{
acc2.lock.writeLock().lock()
acc1.lock.writeLock().lock()
}它实际上可以与tryLock相结合,但可能需要一段时间才能获得这样的锁。
unlock(),这样就不会有对象停留在锁定状态。https://codereview.stackexchange.com/questions/232830
复制相似问题