gpt4 book ai didi

java - 这是实现并发的正确方法吗?

转载 作者:行者123 更新时间:2023-11-30 10:03:55 26 4
gpt4 key购买 nike

我创建了一个小型应用程序,其中基本上包含用于账户间转账的 rest Apis。为简单起见,我对数据存储使用了并发 HashMap 。现在要为此实现的非常基本的概念是多线程。

因为我没有使用任何数据库,所以我使用可重入锁在 Account 类本身上创建了锁。现在,当我进行交易时,我获得了对两个帐户对象(即发送者和接收者)的锁定,然后将其传递给 biconsumer.accept()。

我不确定这是否完全正确(仅就此应用而言。)。也不确定如何为此进行单元测试。

账户.java

public class Account {

private ReentrantLock lock = new ReentrantLock();
private String accountNumber;
private long accountBalance;

public String getAccountNumber() {
return accountNumber;
}

public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}

public long getAccountBalance() {
return accountBalance;
}

public void setAccountBalance(long accountBalance) {
this.accountBalance = accountBalance;
}

public void getLock() {
this.accountLock.lock();
}

public void doUnlock() {
this.accountLock.unlock();
}
}

转账.java

send(senderAccount, receiverAccount, (x, y) -> {
senderAccount.setAccountBalance(senderAccount.getAccountBalance() - (transferAmount));
receiverAccount.setAccountBalance(toAccountDto.getAccountBalance() + transferAmount));
});


public void send(Account senderAccount, Account receiverAccount,
BiConsumer<Account, Account> action) {
senderAccount.lock();
try {
receiverAccount.lock();
try {
biConsumer.accept(senderAccount, receiverAccount);
} finally {
receiverAccount.unlock();
}
} finally {
senderAccount.unlock();
}
}

就单线程而言,这按预期工作得很好。但是我如何对它进行单元测试以检查它是否适用于 10000 个线程。

此外,此帐户级别锁定是否是一种良好做法(就此应用而言),或者我可以做其他事情?

最佳答案

锁是一个不好的工具。

在您发布的示例中,如果两个线程在两个帐户之间汇款,则可能会出现死锁。

final Account acc1 = new Account();
final Account acc2 = new Account();
new Thread(() -> {
while (true) {
send(acc1, acc2, (x, y) -> {
x.setAccountBalance(x.getAccountBalance() - 100);
y.setAccountBalance(y.getAccountBalance() + 100);
});
}
}).start();
new Thread(() -> {
while (true) {
send(acc2, acc1, (x, y) -> {
x.setAccountBalance(x.getAccountBalance() - 100);
y.setAccountBalance(y.getAccountBalance() + 100);
});
}
}).start();

在某个时刻,线程 1 将锁定 acc1,而线程 2 将锁定 acc2

防止这种情况的通常方法是有一个获取锁的特定顺序。
在这种情况下,锁太多而无法管理。

更好的解决方案是使用 AtomicLong ,但这需要对代码进行一些更改。

public class Account {

private String accountNumber;
private AtomicLong accountBalance = new AtomicLong();

public String getAccountNumber() {
return accountNumber;
}

public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}

public long getAccountBalance() {
return accountBalance.get();
}

/* Don't use this method if you expect that the balance has not changed since the last get */
public void setAccountBalance(long accountBalance) {
this.accountBalance.set(accountBalance);
}

/* Changes the balance atomically */
public void addAccountBalance(long amountToAdd) {
accountBalance.addAndGet(amountToAdd);
}
}

然后你可以像这样使用它:

senderAccount.addAccountBalance(-sendAmount);
receiverAccount.addAccountBalance(sendAmount);

因为没有锁,所以不会发生死锁。虽然每个操作都是原子的,但有一些注意事项:

  • 如果您希望旧值是某个数字,例如大于转账金额,您可能不得不在循环中使用 compareAndSet
  • 如果您需要保证所有账户的总和始终保持不变,此方法可能会失败(例如,从一个账户中取出资金,但尚未将其添加到另一个账户)- 在这种情况下,您需要一个大锁。但这会破坏并发性,对吧?

编写多线程代码时最大的问题是找出你想要提供什么保证——或者什么仍然是一致的状态。然后确保没有线程看到系统处于不一致状态。

关于java - 这是实现并发的正确方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56142442/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com