文章详情

背景

假设我们正在开发一个在线银行系统,一个关键功能是用户账户的转账操作。在转账过程中,系统需要检查账户余额是否足够,余额充足,则执行转账;余额不足,则返回错误信息。下面是一个简化的Java代码片段,用于处理转账逻辑:

java

public class TransferService {

public String transferMoney(String fromAccount, String toAccount, double amount) {

double fromBalance = getBalance(fromAccount);

double toBalance = getBalance(toAccount);

if (fromBalance >= amount) {

// 执行转账操作

setBalance(fromAccount, fromBalance – amount);

setBalance(toAccount, toBalance + amount);

return "Transfer successful";

} else {

return "Insufficient funds";

}

}

private double getBalance(String account) {

// 模拟获取账户余额的方法

return 1000.0; // 假设所有账户余额都是1000.0

}

private void setBalance(String account, double newBalance) {

// 模拟设置账户余额的方法

}

}

提出

在上面的代码中,有一个业务逻辑BUG。请这个BUG,并说明如何修复它。

BUG

在上述代码中,`getBalance`方法被调用了两次,分别在获取`fromAccount`和`toAccount`的余额时。账户余额在两次查询之间发生变化(另一个线程正在执行转账操作),`fromBalance`和`toBalance`可能会出现不一致的情况,导致转账失败或者错误地执行转账。

修复方案

为了修复这个BUG,我们可以采取步骤:

1. 使用同步代码块来确保在获取账户余额和更新账户余额时,不会有其他线程操作这些数据。

2. 在同步块内,先更新`fromAccount`的余额,再更新`toAccount`的余额。

3. 在更新`fromAccount`的余额时余额不足,则不需要继续执行转账操作,直接返回错误信息。

下面是修复后的代码:

java

public class TransferService {

public String transferMoney(String fromAccount, String toAccount, double amount) {

synchronized (fromAccount) {

synchronized (toAccount) {

double fromBalance = getBalance(fromAccount);

double toBalance = getBalance(toAccount);

if (fromBalance >= amount) {

// 执行转账操作

setBalance(fromAccount, fromBalance – amount);

setBalance(toAccount, toBalance + amount);

return "Transfer successful";

} else {

return "Insufficient funds";

}

}

}

}

private double getBalance(String account) {

// 模拟获取账户余额的方法

return 1000.0; // 假设所有账户余额都是1000.0

}

private void setBalance(String account, double newBalance) {

// 模拟设置账户余额的方法

}

}

在这个修复方案中,我们使用了两层同步,确保了在执行转账操作时,账户余额的一致性。外层同步块确保了在修改任何账户余额之前,不会有其他线程正在修改账户余额。内层同步块确保了`fromAccount`和`toAccount`的余额获取和更新是原子操作,防止了并发修改的。

通过这样的修改,我们可以确保即使在多线程环境下,转账操作也是线程安全的,可以正确处理余额不足的情况。