我最近遇到了竞争条件问题,因为在默认作用域(单例作用域)服务类中声明了实例变量。实例变量的目的是使我的代码更具可读性,并避免将同一变量不断传递给 Service 类中的不同私有(private)方法。例子如下:
@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
最佳答案
Spring 服务可以是有状态的,这就是作用域的原因。
Spring Service 的默认范围是 Singleton,因为这是最广泛的用例。但您不应该使用可变的局部变量。
简单地说,只有在类实例化速度很快的情况下,尝试无状态设计并使用作用域来解决问题,否则 TreadLocal 的性能会更好。
关于java - 多线程环境下Spring Service实例变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52824586/