繁体   English   中英

线程安全是这里的问题吗?

[英]Is thread safety an issue here?

上下文:我有一个服务 class 有一个公共foo方法(将从 rest 控制器调用),这反过来将调用私有fooHelper传递一个列表fooHelper后填充的其他私有方法调用。 这样做的原因是我想保持foo方法的简短(请建议我创建私有fooHelper的方法是否错误)。

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

我用谷歌搜索并阅读了几篇文章,根据他们的说法,这应该不是问题,因为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()方法。 这些方法应该以线程安全的方式返回其对象底层结构的防御性副本。 然后你的代码将是线程安全的。 另一方面,如果,例如, accounts.getAccounts()只是返回对内部帐户列表的引用,那么当您在foo方法中迭代该列表时,另一个线程可能会动态修改该列表。 所以,确保getAccounts是安全的,然后你的代码是好的。

更新

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

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

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

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

在 thread1 中,一些 thread2 可以修改 accountList,它可以中断foo中此列表的迭代。 您应该创建防御副本,例如:

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

注意synchronized并返回对列表副本的引用,我们以希望线程安全的方式进行(“希望”,因为我们必须确保accounts的所有修改也在同一个 object 上synchronized )。

在您的实现中,您通过执行 REST API 调用来获取列表,该调用应该为每个调用返回一个新列表,独立于另一个。 所以你的实现是安全的。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM