Java 这是实现并发性的正确方法吗?
我创建了一个小应用程序,它基本上包括用于账户间资金转账的RESTAPI。为了简单起见,我将并发hashmap用于数据存储。现在要实现的基本概念是多线程 由于我没有使用任何数据库,所以我使用可重入锁在Account类本身上创建了锁。现在,当我进行交易时,我将获得帐户对象(即发送方和接收方)上的锁,然后将其传递给biconsumer.accept() 我不确定这是否完全正确(仅就本应用而言)。 也不知道如何对此进行单元测试 Account.javaJava 这是实现并发性的正确方法吗?,java,multithreading,junit,reentrantlock,Java,Multithreading,Junit,Reentrantlock,我创建了一个小应用程序,它基本上包括用于账户间资金转账的RESTAPI。为了简单起见,我将并发hashmap用于数据存储。现在要实现的基本概念是多线程 由于我没有使用任何数据库,所以我使用可重入锁在Account类本身上创建了锁。现在,当我进行交易时,我将获得帐户对象(即发送方和接收方)上的锁,然后将其传递给biconsumer.accept() 我不确定这是否完全正确(仅就本应用而言)。 也不知道如何对此进行单元测试 Account.java public class Account {
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();
}
}
Transfer.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();
}
}
send(senderAccount,receiverAccount,(x,y)->{
senderAccount.setAccountBalance(senderAccount.getAccountBalance()-(转账金额));
receiverAccount.setAccountBalance(toAccountDto.getAccountBalance()+转账金额));
});
公共作废发送(账户发送方账户、账户接收方账户、,
双消费者行动){
senderAccount.lock();
试一试{
receiverAccount.lock();
试一试{
双消费者接受(发送方账户、接收方账户);
}最后{
receiverAccount.unlock();
}
}最后{
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
上锁定
防止这种情况的通常方法是按一定的顺序获取锁。在这种情况下,有太多的锁无法管理 更好的解决方案是使用,但这需要对代码进行一些更改
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
- 如果您需要保证所有帐户的总和始终保持不变,则此方法可能会失败(例如,从一个帐户中删除资金,但尚未将其添加到另一个帐户)-在这种情况下,您需要一个大锁。但这将击败并发性,对吗
在编写多线程代码时,最大的问题是找出您想要提供什么样的保证,或者什么样的状态仍然是一致的。然后确保没有线程看到系统处于不一致的状态。您可以求助于:Ok。那将是为了测试目的。但是,如果我这样锁定,这是正确的吗?如果两个帐户互相汇款,您可能会出现死锁。@JohannesKuhn我想如果我使用tryLock(5000,TimeUnit.SECONDS);这应该可以解决问题。对吧?那你也得检查返回值。锁是一罐虫子。当第一个锁成功而第二个锁失败时,您应该怎么做?解锁第一个?等待多久?(一些随机时间可能是最好的答案)。您的系统将很快变得如此复杂,以至于很难对其进行推理。感谢您提供如此详细的答案。你能查一下我贴的那张吗?我们可以通过修改订单或锁来解决这样的问题吗?