Wednesday, December 31, 2008

Double Check in Java is broken .. not

Coders from the C world are most likely to use the so called double check idiom at some point of their coding career. If those same coders (like me) do their first multithreaded app in java will probably run into some problems.After my first trials with java several years ago I ran into some pretty weird problems. Sometimes my nice apps would expose the weirdest behaviour. After searching around I got educated in a newsgroup.
Before I start to explain what the problem is I should probably explain what double-check is.
Let's say there is a certain object that needs to be initialized. An initialization should only take place once. Sometimes it might be required to postpone the initialization until the first use of the object (lazy init) or until certain conditions in the systems are met (service orientated architectures).
The following piece of code illustrates the naive approach:>

Test initValue=null;

if(initValue==null){
synchronized(this){
if(initValue==null){
initValue=new Test();
}
}
}
So we first check if the variable has been initialized, if not we synchronize and check again and if this second check is succeeds we do the init work. The second check is done because another thread might have initilaized in the meantime.
The problem is that this won't work in Java. The JDK does a lot of work under the hood. On of these things is that threads can keep a local copy of variables. This behavior completely breaks double check.
That's where my story ended some years ago. I never touched it or bothered researching into it again.
Until I recently discovered atomic variables in Java and stumbled over the new memory model introduced with JDK 5.
After reading up on it I finally found the solution to fix double-check:
The volatile keyword.
Volatile indicates that the variable will be modified by several threads. This tells the JVM that threads are simply not allowed to keep local copies of variables.
Fixed double check locking looks like this:
volatile Test initValue=null;<br />if(initValue==null){<br />    synchronized(this){<br />        if(initValue==null){<br />            initValue=new Test();<br />        }<br />    }<br />}

Yep, I got my double-check locking back but there are still some things to consider:
  • Performance: If using volatile on a variable better do some benchmarking.
  • JDK 5: Yep, volatile only works like this with JDK 5 and later.



3 comments:

Unknown said...

Hi Jochen,

How about the following Single ton construct:

private static MyTest test;
public static MyTest getInstance(){
if (tets == null) {
test = createNewInstance();
}
return test;
}

private static synchronized MyTest createNewInstance() {
if (test == null) {
MyTest newTest = new MyTest();
return newTest;
}
return test;
}

Jochen Mader said...

Sorry, I overlooked your comment. Your sourcecode is an example that will only work in a single threaded environment. Just think about the following scenario:
Thread nr 1 enters the getInstance method, the check allows it to enter the if block. Now the scheduler suspends the thread and Thread nr 2 enters getInstance, as the first thread has not yet finished the call it will be able to also enter the if block. Now you have two threads in there and two instances will be created.

Bill said...
This comment has been removed by the author.