线程安全在这里是一个问题吗?

Is thread safety an issue here?

上下文: 我有一个服务 class,它有一个 public foo 方法(将从休息控制器调用) ,这又将调用 private fooHelper 传递一个列表对象,该对象在 fooHelper 中的其他几个私有方法调用后被填充。这样做的原因是我想让 foo 方法简短(如果我的方法对于创建私有 fooHelper 是错误的,请提出建议)。

问题:这里线程安全会成为一个问题吗?如果多个线程同时访问 foo 方法(参考传递的列表对象)?我知道我可以使用 Unmodifiable 列表,但我的问题是特定于当前方法是否存在线程安全问题的场景?

我用谷歌搜索并阅读了几篇文章,根据他们的说法,这应该不是问题,因为 foo 正在创建 new List,因此没有线程会共享相同的列表。我的理解正确吗?

任何帮助将不胜感激,如果我无法清楚地提出我的问题,也请原谅。欢迎任何编辑或建议。

@RequiredArgsConstructor
@Service
public class A {


    @Override
    public List<RoundUpResponse> foo(String token) {

        final List<RoundUpResponse> roundUpResponseList = new ArrayList<>();
        final Accounts accounts = getAccounts(token);
        final List<Account> accountList = accounts.getAccounts();

        accountList.forEach(account -> fooHelper(token, roundUpResponseList, account));
        return roundUpResponseList;
    }

private void fooHelper(String token, List<RoundUpResponse> roundUpResponseList, Account account) {
        final FeedItems transactionFeed = getTransactionFeed(token, account); // private
        final SavingsGoal savingsGoal = getSavingsGoal(token, account); // private
        final Long spareChange = calculateSpareChange(transactionFeed, savingsGoal); // private
        final SavingsGoalTopUp savingsGoalTopUpPayload = createTopUpPayload(account, // privatespareChange);
        final String savingsGoalTransferId = topUpSavingsGoal(token, account, savingsGoal, savingsGoalTopUpPayload); // private
        roundUpResponseList.add(roundUpResponseService.createResponsePerAccount(account, savingsGoal.getName(), savingsGoalTopUpPayload, savingsGoalTransferId)); // private
    }

代码可能是线程安全的,也可能不是线程安全的,具体取决于 getAccounts() 方法。这些方法应该 return 以线程安全的方式对其对象的底层结构进行防御性复制。那么你的代码将是线程安全的。另一方面,例如,如果 accounts.getAccounts() 只是 return 对内部帐户列表的引用,那么当您在 foo 方法。所以,确保 getAccounts 是安全的,然后你的代码是好的。

更新

具有不同的 getAccounts() 调用堆栈与其线程安全无关。这是一个非线程安全的实现:

class Accounts {
    private List<Account> accounts;
    public List<Account> getAccounts() {
        return accounts;
    }
}

在这种情况下,每个调用线程都将获得对内部结构的相同引用,因此在您在 foo 方法中执行

final List<Account> accountList = accounts.getAccounts();

在线程 1 中,某些线程 2 可以修改 accountList,它可以在 foo 中中断对该列表的迭代。您应该创建防御性副本,例如:

class Accounts {
    private List<Account> accounts;
    public List<Account> getAccounts() {        
        synchronized(this) {  
          return new ArrayList<>(accounts);
        }
    }
}

注意 synchronized 和 returning 对列表副本的引用,我们以希望线程安全的方式(“希望”,因为我们必须确保所有accounts 的修改也在同一对象上 synchronized

在您的实现中,您通过执行 REST API 调用来获取列表,这应该 return 每个调用都有一个独立于其他调用的新列表。所以你的实施是安全的。