user1713946
user1713946

Reputation: 93

Spring race conditions singleton service stateful or stateless

I am a little concerned about race conditions using Spring. I know the differences between all kind of scopes (singleton, prototype, session and so on)

I also know that:

Even though I am not 100 percent sure about this stateless thing. The sources of my research always just focus on shared instance variables when talking about statefulness, but race conditions can not only arise when accessing instance variables. I created the following example to illustrate my problem:

@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));
  } 
}

You can assume that the service is used in a rest controller or something similar. From my understanding there can be a problem with race conditions when the method existsAppleInCategory is called. For example thread1 has its slot and writes "false" to the variable existsAppleInCategory. Then thread 2 has its slot overwriting existsAppleInCategory with "true". Afterwards thread 1 has another time slot. => thread 1 now "does something" instead of throwing a RuntimeException.

Is my assumption correct? Do I have problems with race conditions in this scenario? If not why? Can you possibly recommend any sources writing about this topic in detail (online sources, books, ...)?

Thank you in advance!

Upvotes: 5

Views: 6491

Answers (4)

M. Deinum
M. Deinum

Reputation: 124461

Assuming that the AppleRepository is a stateless singleton and leaving out the database in this (as your question regards java).

In short thread1 cannot see the variables from thread2 so you don't have any concurrency issues.

Each executing thread has its own stack, on that stack are, amongst others, the variables used inside the method so thread1 cannot see the existsAppleInCategory from thread2 and vice-versa. The variable is local to the method (actually block of execution) and each thread has its own copy on the stack.

This would be different if existsAppleInCategory would be an instance level variable as you then have shared state. In that case it could be possible that threads see each others data (depending on when the state is written/read and the use of the volatile keyword).

Upvotes: 8

Serge Ballesta
Serge Ballesta

Reputation: 148880

As M.Deinum said in its comment your code does not exhibit any race condition problem, because existsAppleInCategory is a variable local to a function and not an instance variable. Each thread will have its own copy and all will be fine.

This would be quite different

@Service
public class AppleService {

  @Autowired
  private AppleRepository appleRepository;

  private boolean _existsAppleInCategory;

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

because now you are changing an instance variable that will be shared by all threads.

Upvotes: 4

Bhuwan Prasad Upadhyay
Bhuwan Prasad Upadhyay

Reputation: 3056

Yes, here is possible for race condition from database level.

If you query data and then insert or update related data within the same transaction, the regular SELECT statement does not give enough protection. Other transactions can update or delete the same rows you just queried.

So use database Lock approaches using

Locking Reads (SELECT ... FOR UPDATE and SELECT ... LOCK IN SHARE MODE)

If you want to protect race condition in java used synchronization in code level https://docs.oracle.com/javase/tutorial/essential/concurrency/sync.html

More:

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

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

Upvotes: 0

malaguna
malaguna

Reputation: 4233

In your example, you can consider that appleRepository has state, because as you describe in your example, it can change.

Thus you have to consider thread safeness in this class also (maybe serializing?). Obviously, your AppleService is thread safe because it is a stateless singleton. Your race condition problem will come from the repository, not from the singleton.

A nice and basic read Java Tutorial Concurrency

Upvotes: 0

Related Questions