Reputation: 93
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
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
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
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
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