Spring is a great framework, but I expect that some Spring based applications are subject to visibility problems (a type of concurrency problem). This blog entry describes the cause and effects of this problem, and also how it can be solved.
Most programmers don't have much experience with multi threading and have a very simplistic view on reality: if a thread makes a change to a variable, this change is directly visible to all other threads that access the same variable. This view is called sequential consistency, but the problem is that no virtual machine automatically provides this view.
The reason for this apparently faulty behavior is performance. Most variables are not shared between threads, so no special instructions (memory barriers) have to be added to make the program sequentially consistent. If these instructions were automatically added to all variable accesses, it would greatly reduce performance because a lot of optimizations would be excluded from being used. To name a few:
The consequence of a visibility problem, is that a value written to a variable by one thread, doesn't have to be visible in other threads (maybe never). This could lead to an easy to detect NullPointerException, but it can also lead to much harder to detect problems like shown in the following example:
public class SomeRunnable implements Runnable{
private boolean stop = false;
public void stop(){
stop = true;
}
public void run(){
while(!stop){
System.out.println("hello");
}
}
}
There are a few reasons why this runnable could fail to stop:
Visibility problems can lead to hard to detect errors and unpredictable behavior, and you definitely want to keep them out of your system. And although most cpu's are not subject to visibility problems (because of strong Memory Coherence), betting on this behavior is asking for problems (the application also wouldn't be platform independent).
I expect that a lot of Spring applications are subject to visibility problems. If you look at singleton beans for example (dao's, controllers, managers etc), these beans often are shared by many threads, to name a few:
Take a look at the following example:
public class EmployeeManagerImpl implements EmployeeManager{
private EmployeeDao employeeDao;
public setEmployeeDao(EmployeeDao employeeDao){
this.employeeDao = employeeDao;
}
public void fire(long employeeId){
Employee employee = employeeDao.load(employeeId);
employee.fire();
}
}
And the bean configuration that belongs to it:
<bean id="employeeManager" class="EmployeeManagerImpl">
<property name="employeeDao" ref="employeeDao"/>
</bean>
The visibility problem here, is that employeeDao, set by the thread that constructed the employeeManager, isn't safely published and doesn't have to be visible in other threads. If one of those threads calls the fire method on the EmployeeManagerImpl, it could lead to a NullPointerException because that thread still sees a null value for the employeeDao.
The problem can be solved in a few ways:
Before going into detail why the problems maybe are not that bad, I need to explain the new Memory Model found in Java 5 and higher (JSR-133) (see footnote). The model describes under what kinds of conditions a variable will be visible in other threads and it also shields you from reasoning about caches (they are not mentioned in the Java Memory Model).
The model is expressed in terms of actions:If there is a happens before relation between the write and the read of a variable, then it is safely published.
These happens-before rules can be used to check if there is a happens-before relation between two actions. Take a look at the following example:
int a=0;
volatile boolean b=0;
void initialize(){
a=console.readInteger();
b=console.readBoolean();
}
void print(){
print(b);
print(a);
}
A possible ordering of actions could be:
| action | thread 1 | thread 2 |
| action1 | normalwrite(a) | |
| action2 | volatilewrite(b) | |
| action3 | volatileread(b) | |
| action4 | normalread(a) |
Because action1 happens-before action2 (program order) and because action2 happens-before action3 (volatile variable rule) and because action3 happens before action4 (program order), action1 happens before action4 (remember that the happens before rules are transitive). This means that the change in action1, is visible in action4. This technique is called 'safe handoff' (aka 'piggybacking on synchronization').
Safe handoff uses the safe publication of a variable X (in this case b), to safely publish all unsafely-published-variables that are changed before X (in this case a).
The Spring applicationcontext also provides safe handoff (only in Java 5 and higher): after a singleton bean is created, it is placed in a singletonmap. When it is needed, it is retrieved from that map. But instead of using the 'volatile variable' rule, it uses the 'monitor lock' rule (action4 and action5)
The following table shows a very simplified ordering of actions (variable mapentry.ref is the variable 'X' from the 'save handoff' technique):
| action | thread 1 | thread 2 |
| action1 | lock(singletonmap) | |
| construction of the employeeManager | ||
| action2 | normalwrite(employeeDao) | |
| employeeManager is placed in the applicationcontext | ||
| action3 | normalwrite(mapentry.ref) | |
| action4 | unlock(singletonmap) | |
| employeeManager is read from the applicationcontext | ||
| action5 | lock(singletonmap) | |
| action6 | normalread(mapentry.ref) | |
| action7 | unlock(singletonmap) | |
| employeeManager fire method is called | ||
| action8 | normalread(employeeDao) |
This means that there is a happens before relation between action2 and action8, so the value set in the employeeDao (action2), is visible when it is read by another thread (action8). That is why standard Spring singletons don't have visibility problems. If you want to see it for yourself, check out the DefaultSingletonBeanRegistry and the 'public Object getSingleton(String beanName, ObjectFactory singletonFactory)' method
I'm glad that standard singleton beans aren't subject to visibility problems (under Java 5 and higher), but I don't think it is the correct way to deal with concurrency control in Spring (or in general):
I don't want to depict Spring as a bad product: it really is amazing and I'm always happy when I can use it on a project. But I do think that developers should be more careful when writing code that is used in a multi threaded environment.
A part of this responsibility should be taken bij developers themselves, if you write threadsafe code, you need to know what you are doing. But I also think the guys behind Spring should take part of this responsibility: not all developers have a lot of experience with writing multi-threaded code and I guess most don't know anything about the Java Memory Model. If these issues are not mentioned in the Spring documentation, a lot of developers will remain agnostic and this is going to lead to problems in the future. Footnote:Filed under Concurrency Control, Java, Spring | 6 Comments »
Please stop describing multi-threading
programming as ’something that only aliens knows how to use… ‘
And stop also speak about Spring as something that has a lot of problems, we are using spring in our E-commerce web site from different years and we haven’t yet any concurrency problem!
Hi Indrit, thank you for your reply
As far as I know I didn’t depict Spring as a bad framework (a framework with a lot of problems), so I don’t understand how you come to that conclusion.
And multi-threading has a lot of complicated facets not a lot of people know about, for example the memory model or the difference between the memory models before and after jsr133. If you don’t know about these issues, concurrent programming is a lucky guess at best.
Did you read the complete article?
Good article indeed, concurrency and multi-threadings are not easy, if you can’t fully understand that, as Peter says, U r just lucky,hehe
I am a big funs of spring, but i don’t think the article is blaming or denigrating it.
Interesting article, thanks
Great article!
Many developers aren’t aware of visibility issues that may occur in a multi-threaded environment. Because of the potential for such issues when using setter injection I tend to prefer constructor injection. Spring promotes setter injection and indeed there are good reasons for this. So i’m often torn between the two injection styles.
Greetings from Germany!
I forgot to mention that i prefer constructor injection in conjunction with — final — member variables because these are guaranteed to be published safely by the Java memory model.