V
Vinay Aggarwal
I have been thinking about the lazy initialization and double checked
locking problem. This problem is explain in detail here
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
I am not fully convinced that this problem cannot be
solved. I am going to propose a solution here. For the sake of
discussion I will post my solution here. It is possible that the
proposed solution does not work, feedback and comments are welcome.
Here is the original problem. We need to lazy initialize a non static
variable to an instance of a heavy object in a thread safe way. (It is
very easy to safely initialize a static variable). The most commonly
used code is as follows:
---------------------------------------------------------------------------
public class LazyInit
{
private HeavyObject heavyObject =
null;
public HeavyObject
getHeavyObject()
{
if (heavyObject ==
null)
{
synchronized(this)
{
if (heavyObject == null)
{
heavyObject = new HeavyObject();
}
}
}
return
heavyObject;
}
}
---------------------------------------------------------------------------
Unfortunately, this code is broken as described at
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html.
The problem arises from the fact that the optimising compilers can
reorder the constructor call and the assignment to the heavyObject
variable.
In my proposed solution, I am going to use polymorphism to appropriately
syncronize the initialization and after its safe initialization, replace
the implementation itself to be unsynchronized. Here is the code,
detailed description follows.
---------------------------------------------------------------------------
import java.util.*;
public class
LazyInit
// line 3
{
private static HeavyObjectInit heavyObjectInitStatic = new
HeavyObjectInit(); // line 5
private HeavyObjectInit heavyObjectInit = new
HeavyObjectInitSync(); // line 6
private volatile HeavyObject heavyObject =
null; // line 7
static class HeavyObjectInitSync extends
HeavyObjectInit // line 9
{
public HeavyObject getHeavyObject(LazyInit
lazyInit) // line 11
{
HeavyObject heavyObject =
getHeavyObjectInternal(lazyInit); // line 13
lazyInit.heavyObjectInit =
heavyObjectInitStatic; // line 14
return
heavyObject;
// line 15
}
private synchronized HeavyObject getHeavyObjectInternal(LazyInit
lazyInit) // line 18
{
if(lazyInit.heavyObject ==
null) // line 20
{
lazyInit.heavyObject = new
HeavyObject(); // line 22
}
return
lazyInit.heavyObject;
// line 24
}
}
static class
HeavyObjectInit
// line 29
{
public HeavyObject getHeavyObject(LazyInit
lazyInit) // line 31
{
return
lazyInit.heavyObject;
// line 33
}
}
public HeavyObject
getHeavyObject()
// line 37
{
return
heavyObjectInit.getHeavyObject(this);
// line 39
}
}
---------------------------------------------------------------------------
Explanation:
Lets assume this code is executing in a multi threading environment. Now
lets say a few threads enter LazyInit.getHeavyObject() at line 37
simultaneously. These threads will reach
heavyObjectInit.getHeavyObject() at line 39. Since heavyObjectInit is
initialized to HeavyObjectInitSync (at line 6), these threads will enter
getheavyObject() at line 11. At line 13, only one thread (lucky thread)
will be able to enter the syncronized method getHeavyObjectInternal
while rest of threads will be blocked at line 18. The lucky thread will
initialize the heavy object and return it. Since the thread is exiting a
syncronized method and the variable is volatile, the HeavyObject will be
fully initialized before the thread releases the lock. Now the lucky
thread will switch the implementation of heavyObjectInit to a non
syncronized initializer (line 14). Any threads reaching line 39 after
this will call the non syncronized version of getHeavyObject() at line
31. At the same time, all the threads blocked at line 18 will enter the
getHeavyObjectInternal() method one by one and return with the singleton
heavy object instance.
Thus initially a few threads will be synchronized till the lucky thread
switches the initializer. At this time the system will switch to non
syncronized implementation. Note that non syncronized implementation
(line 33) does not even incur the cost of null check as compared to the
original algorithm.
There are some intricacies that I have tried to explain in Q&A form.
Why is the instance of HeavyObjectInitSync nonstatic while instance of
HeavyObjectInit static?
If the HeavyObjectInitSync instance is made static, the syncronized
method will syncronize on the single instance of the class. Since all
instances of LazyInit will refer to single instance of
HeavyObjectInitSync, the call will be mutually exclusive across ALL
instances of LazyInit. Essentially if there are 1000 LazyInit objects
each one wanting to initialize HeavyObjects, these objects will get
initialized sequentially, thus slowing down the process. By making the
instance non static, different instances of LazyInit can initialize the
HeavyObject in parallel.
The HeavyObjectInit instance is static because we dont need one instance
of the HeavyObjectInit class with every instance of LazyInit. This
eliminates unnecessary object creation.
Why is HeavyObjectInitSync class static?
This class can be static or non-static. It is a matter of personal
choice. It should work both ways. I wanted to be consistent with
HeavyObjectInit class.
I have a code sample that shows how different threads will execute.
Download it here http://24.167.121.42/LazyInit.java
The output of this program demonstrates
1. Different threads entering one instance of LazyInit get blocked till
the HeavyObject is initialized.
2. Different threads entering different instances of LazyInit can
initialize the heavy object simultaneously.
3. The HeaveyObject is initialized only once per instance of LazyInit.
4. After the initialization is complete, any other threads entering that
instance of LazyInit do not syncronize anymore.
Vinay Aggarwal
CTO Techlobby
vinay at t e c h l o b b y dot com
h t t p : / / w w w. t e c h l o b b y . c o m /
locking problem. This problem is explain in detail here
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
I am not fully convinced that this problem cannot be
solved. I am going to propose a solution here. For the sake of
discussion I will post my solution here. It is possible that the
proposed solution does not work, feedback and comments are welcome.
Here is the original problem. We need to lazy initialize a non static
variable to an instance of a heavy object in a thread safe way. (It is
very easy to safely initialize a static variable). The most commonly
used code is as follows:
---------------------------------------------------------------------------
public class LazyInit
{
private HeavyObject heavyObject =
null;
public HeavyObject
getHeavyObject()
{
if (heavyObject ==
null)
{
synchronized(this)
{
if (heavyObject == null)
{
heavyObject = new HeavyObject();
}
}
}
return
heavyObject;
}
}
---------------------------------------------------------------------------
Unfortunately, this code is broken as described at
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html.
The problem arises from the fact that the optimising compilers can
reorder the constructor call and the assignment to the heavyObject
variable.
In my proposed solution, I am going to use polymorphism to appropriately
syncronize the initialization and after its safe initialization, replace
the implementation itself to be unsynchronized. Here is the code,
detailed description follows.
---------------------------------------------------------------------------
import java.util.*;
public class
LazyInit
// line 3
{
private static HeavyObjectInit heavyObjectInitStatic = new
HeavyObjectInit(); // line 5
private HeavyObjectInit heavyObjectInit = new
HeavyObjectInitSync(); // line 6
private volatile HeavyObject heavyObject =
null; // line 7
static class HeavyObjectInitSync extends
HeavyObjectInit // line 9
{
public HeavyObject getHeavyObject(LazyInit
lazyInit) // line 11
{
HeavyObject heavyObject =
getHeavyObjectInternal(lazyInit); // line 13
lazyInit.heavyObjectInit =
heavyObjectInitStatic; // line 14
return
heavyObject;
// line 15
}
private synchronized HeavyObject getHeavyObjectInternal(LazyInit
lazyInit) // line 18
{
if(lazyInit.heavyObject ==
null) // line 20
{
lazyInit.heavyObject = new
HeavyObject(); // line 22
}
return
lazyInit.heavyObject;
// line 24
}
}
static class
HeavyObjectInit
// line 29
{
public HeavyObject getHeavyObject(LazyInit
lazyInit) // line 31
{
return
lazyInit.heavyObject;
// line 33
}
}
public HeavyObject
getHeavyObject()
// line 37
{
return
heavyObjectInit.getHeavyObject(this);
// line 39
}
}
---------------------------------------------------------------------------
Explanation:
Lets assume this code is executing in a multi threading environment. Now
lets say a few threads enter LazyInit.getHeavyObject() at line 37
simultaneously. These threads will reach
heavyObjectInit.getHeavyObject() at line 39. Since heavyObjectInit is
initialized to HeavyObjectInitSync (at line 6), these threads will enter
getheavyObject() at line 11. At line 13, only one thread (lucky thread)
will be able to enter the syncronized method getHeavyObjectInternal
while rest of threads will be blocked at line 18. The lucky thread will
initialize the heavy object and return it. Since the thread is exiting a
syncronized method and the variable is volatile, the HeavyObject will be
fully initialized before the thread releases the lock. Now the lucky
thread will switch the implementation of heavyObjectInit to a non
syncronized initializer (line 14). Any threads reaching line 39 after
this will call the non syncronized version of getHeavyObject() at line
31. At the same time, all the threads blocked at line 18 will enter the
getHeavyObjectInternal() method one by one and return with the singleton
heavy object instance.
Thus initially a few threads will be synchronized till the lucky thread
switches the initializer. At this time the system will switch to non
syncronized implementation. Note that non syncronized implementation
(line 33) does not even incur the cost of null check as compared to the
original algorithm.
There are some intricacies that I have tried to explain in Q&A form.
Why is the instance of HeavyObjectInitSync nonstatic while instance of
HeavyObjectInit static?
If the HeavyObjectInitSync instance is made static, the syncronized
method will syncronize on the single instance of the class. Since all
instances of LazyInit will refer to single instance of
HeavyObjectInitSync, the call will be mutually exclusive across ALL
instances of LazyInit. Essentially if there are 1000 LazyInit objects
each one wanting to initialize HeavyObjects, these objects will get
initialized sequentially, thus slowing down the process. By making the
instance non static, different instances of LazyInit can initialize the
HeavyObject in parallel.
The HeavyObjectInit instance is static because we dont need one instance
of the HeavyObjectInit class with every instance of LazyInit. This
eliminates unnecessary object creation.
Why is HeavyObjectInitSync class static?
This class can be static or non-static. It is a matter of personal
choice. It should work both ways. I wanted to be consistent with
HeavyObjectInit class.
I have a code sample that shows how different threads will execute.
Download it here http://24.167.121.42/LazyInit.java
The output of this program demonstrates
1. Different threads entering one instance of LazyInit get blocked till
the HeavyObject is initialized.
2. Different threads entering different instances of LazyInit can
initialize the heavy object simultaneously.
3. The HeaveyObject is initialized only once per instance of LazyInit.
4. After the initialization is complete, any other threads entering that
instance of LazyInit do not syncronize anymore.
Vinay Aggarwal
CTO Techlobby
vinay at t e c h l o b b y dot com
h t t p : / / w w w. t e c h l o b b y . c o m /