多线程环境中的Spring Service实例变量

时间:2018-10-15 20:50:24

标签: java spring multithreading spring-mvc thread-safety

我最近遇到了竞争状况问题,因为在默认范围(Singleton范围)Service类中声明了一个实例变量。实例变量的目的是使我的代码更具可读性,并避免将同一变量不断传递给Service类中的不同私有方法。示例如下:

@Service
public class SomeServiceImpl implements SomeService {
    private final StatusRepository statusRepository;
    private Predicate<Status> statusPredicate;

    @Autowired
    public SomeServiceImpl(StatusRepository statusRepository) {
        this.statusRepository = statusRepository;
    }

    @Override
    public List<Status> getAllowedStatuses(String userId) {
        statuses = statusRepository.getAll();
        initPredicate();
        appendPredicateA();
        appendPredicateB();
        List<Status> results = statuses.stream()
            .filter(statusPredicate)
            .collect(Collectors.toList());
        return results;
    }

    private void initPredicate() {
        statusPredicate = p -> p.getDefault().equals("default");
    }

    private void appendPredicateA() {
        statusPredicate.and(p -> p.getA().equals("A"));
    }

    private void appendPredicateB() {
        statusPredicate.and(p -> p.getB().equals("B"));
    }
}

这是我要实现的目标的非常简单的示例。这显然不是线程安全的,因为现在服务类是有状态的。我可以通过将 statusPredicate 变量转换为局部变量并让 void 方法返回谓词来解决此问题在附加了新条件之后,但是这样会变得混乱:

@Override
public List<Status> getAllowedStatuses(String userId) {
    statuses = statusRepository.getAll();
    Predicate<Status> statusPredicate = p -> p.getDefault().equals("default");
    statusPredicate = appendPredicateA(statusPredicate);
    statusPredicate = appendPredicateB(statusPredicate);
    List<Status> results = statuses.stream()
        .filter(statusPredicate)
        .collect(Collectors.toList());
    return results;
}

它将不断地调用以修改变量并返回变量。

我知道一些可以解决此问题的解决方案,例如在Service类上添加 @RequestScope ,以确保来自HTTP的每个请求都将获得一个新的 服务对象,或在Predicate变量上使用 ThreadLocal 。但是,我不确定最好的方法是什么,以及不确定是否可以在Service类中声明实例变量。如果一开始让Service类成为有状态是不好的,那么我应该如何构造代码使其更整洁并保持无状态呢?

请指教!在此先感谢:D

1 个答案:

答案 0 :(得分:0)

Spring Service可以是有状态的,这就是为什么要作用域的原因。

Spring Service的默认范围是Singleton,因为这是最广泛的用例。但是您不应该使用可变的局部变量。

简单地,尝试无状态设计并仅在类实例化速度快的情况下使用范围来解决问题,否则TreadLocal的性能会更好。