春季比赛条件单身人士服务有状态或无国籍

时间:2015-09-25 09:58:02

标签: java multithreading spring spring-mvc race-condition

我有点担心使用Spring的竞争条件。 我知道所有类型的范围(单例,原型,会话等)之间的差异

我也知道:

  • 默认情况下,服务具有单一作用域
  • 只要我的豆无国籍,我就不会遇到种族问题

即使我不是百分之百地确定这种无国籍的事情。在讨论有状态时,我的研究来源总是专注于共享实例变量,但竞争条件不仅可以在访问实例变量时出现。 我创建了以下示例来说明我的问题:

@Service
public class AppleService {

  @Autowired
  private AppleRepository appleRepository;

  @Override
  public void doSomethingWithAppleCategory(String appleCategory) {
    boolean existsAppleInCategory = existsAppleInCategory(appleCategory);
    if(existsAppleInCategory) {
        // do something
    }
    else {
        throw new RuntimeException("There is no apple in the category: " + appleCategory);
    }
  }

  private boolean existsAppleInCategory(String appleCategory) {
    Iterable<Apple> allApples = appleRepository.findAll();
    return allApples.stream().anyMatch(a -> a.getAppleCategory().equals(appleCategory));
  } 
}

您可以假设该服务用于休息控制器或类似的东西。根据我的理解,当调用方法existsAppleInCategory时,竞争条件可能存在问题。例如,thread1有其槽,并将“false”写入变量existsAppleInCategory。然后线程2将其插槽覆盖existsAppleInCategory并使用“true”。之后,线程1有另一个时隙。 =&GT;线程1现在“做某事”而不是抛出RuntimeException。

我的假设是否正确?在这种情况下,我是否遇到过竞争条件问题? 如果不是为什么?您是否可以推荐任何有关此主题的资料(在线资源,书籍......)?

提前谢谢!

4 个答案:

答案 0 :(得分:2)

假设AppleRepository是一个无状态单例并且在此中省略了数据库(因为你的问题是关于java)。

简而言之,thread1无法从thread2中看到变量,因此您不会遇到任何并发问题。

每个执行线程都有自己的堆栈,在该堆栈中,其中包括方法中使用的变量,因此thread1无法从thread2中看到existsAppleInCategory,反之亦然。该变量是方法的本地变量(实际上是执行块),每个线程在堆栈上都有自己的副本。

如果existsAppleInCategory是实例级变量,那么这将是不同的,因为您具有共享状态。在这种情况下,线程可能会看到彼此的数据(取决于写入/读取状态的时间以及volatile关键字的使用情况)。

答案 1 :(得分:1)

正如M.Deinum在评论中所说,你的代码没有出现任何竞争条件问题,因为existsAppleInCategory是函数的局部变量而不是实例变量。每个帖子都有自己的副本,一切都会好的。

这将是完全不同的

@Service
public class AppleService {

  @Autowired
  private AppleRepository appleRepository;

  private boolean _existsAppleInCategory;

  @Override
  public void productCallDetection(String appleCategory) {
    _existsAppleInCategory = existsAppleInCategory(appleCategory);
    if(existsAppleInCategory) {
        // do something
    }
    ...
  }
  ...
}

因为现在您正在更改将由所有线程共享的实例变量。

答案 2 :(得分:0)

在您的示例中,您可以认为appleRepository具有状态,因为正如您在示例中所描述的那样,它可以更改。

因此,您还必须考虑此类中的线程安全性(可能是序列化?)。显然,你的AppleService是线程安全的,因为它是一个无状态的单例。您的竞争条件问题将来自存储库,而不是来自单例。

一个不错的基本阅读Java Tutorial Concurrency

答案 3 :(得分:0)

是的,这可能是数据库级别的竞争条件。

  

如果查询数据然后插入或更新相关数据   同样的事务,常规的SELECT语句不够用   保护。其他事务可以更新或删除您的相同行   只是查询。

因此使用数据库锁定方法

  

锁定读取(SELECT ... FOR UPDATE和SELECT ... LOCK IN SHARE   MODE)

如果您想保护代码级https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html中使用synchronization的java中的竞争条件

更多

Mysql:http://dev.mysql.com/doc/refman/5.6/en/innodb-locking-reads.html

PlSQL:http://www.techonthenet.com/oracle/cursors/for_update.php