Solving Lazy Initialization and double checked locking problem

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Vinay Aggarwal

    Solving Lazy Initialization and double checked locking problem


    I have been thinking about the lazy initialization and double checked
    locking problem. This problem is explain in detail here

    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(th is)
    {
    if (heavyObject == null)
    {
    heavyObject = new HeavyObject();
    }
    }
    }
    return
    heavyObject;
    }
    }

    ---------------------------------------------------------------------------

    Unfortunately, this code is broken as described at
    http://www.cs.umd.edu/~pugh/java/mem...edLocking.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 heavyObjectInit Static = new
    HeavyObjectInit (); // line 5
    private HeavyObjectInit heavyObjectInit = new
    HeavyObjectInit Sync(); // line 6
    private volatile HeavyObject heavyObject =
    null; // line 7

    static class HeavyObjectInit Sync extends
    HeavyObjectInit // line 9
    {
    public HeavyObject getHeavyObject( LazyInit
    lazyInit) // line 11
    {
    HeavyObject heavyObject =
    getHeavyObjectI nternal(lazyIni t); // line 13
    lazyInit.heavyO bjectInit =
    heavyObjectInit Static; // line 14
    return
    heavyObject;
    // line 15
    }

    private synchronized HeavyObject getHeavyObjectI nternal(LazyIni t
    lazyInit) // line 18
    {
    if(lazyInit.hea vyObject ==
    null) // line 20
    {
    lazyInit.heavyO bject = new
    HeavyObject(); // line 22
    }
    return
    lazyInit.heavyO bject;
    // line 24
    }

    }

    static class
    HeavyObjectInit
    // line 29
    {
    public HeavyObject getHeavyObject( LazyInit
    lazyInit) // line 31
    {
    return
    lazyInit.heavyO bject;
    // 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.getHea vyObject() at line 37
    simultaneously. These threads will reach
    heavyObjectInit .getHeavyObject () at line 39. Since heavyObjectInit is
    initialized to HeavyObjectInit Sync (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 getHeavyObjectI nternal
    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
    getHeavyObjectI nternal() 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 HeavyObjectInit Sync nonstatic while instance of
    HeavyObjectInit static?
    If the HeavyObjectInit Sync 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
    HeavyObjectInit Sync, 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 HeavyObjectInit Sync 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 /
  • Anton Spaans

    #2
    Re: Solving Lazy Initialization and double checked locking problem

    Hi Vinay,

    Unless 'synchronized' blocks cause unacceptable performance issues, just use
    them.
    Otherwise, take a look at this:



    tml

    We use it in our software, for lazy caching of certain objects (using
    ConcurrentReade rHashMap).
    It works great! And the use of this package is free. No license needed.

    -- Anton.

    "Vinay Aggarwal" <vinayagg@h-o-t-m-a-i-l.com> wrote in message
    news:XwMxb.5153 0$Ek.6439@twist er.austin.rr.co m...[color=blue]
    >
    > 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/mem...edLocking.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:
    > --------------------------------------------------------------------------[/color]
    -[color=blue]
    > public class LazyInit
    > {
    > private HeavyObject heavyObject =
    > null;
    >
    > public HeavyObject
    > getHeavyObject( )
    > {
    > if (heavyObject ==
    > null)
    > {
    > synchronized(th is)
    > {
    > if (heavyObject == null)
    > {
    > heavyObject = new HeavyObject();
    > }
    > }
    > }
    > return
    > heavyObject;
    > }
    > }
    >
    > --------------------------------------------------------------------------[/color]
    -[color=blue]
    >
    > Unfortunately, this code is broken as described at
    > http://www.cs.umd.edu/~pugh/java/mem...edLocking.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.
    >
    > --------------------------------------------------------------------------[/color]
    -[color=blue]
    > import java.util.*;
    >
    > public class
    > LazyInit
    > // line 3
    > {
    > private static HeavyObjectInit heavyObjectInit Static = new
    > HeavyObjectInit (); // line 5
    > private HeavyObjectInit heavyObjectInit = new
    > HeavyObjectInit Sync(); // line 6
    > private volatile HeavyObject heavyObject =
    > null; // line 7
    >
    > static class HeavyObjectInit Sync extends
    > HeavyObjectInit // line 9
    > {
    > public HeavyObject getHeavyObject( LazyInit
    > lazyInit) // line 11
    > {
    > HeavyObject heavyObject =
    > getHeavyObjectI nternal(lazyIni t); // line 13
    > lazyInit.heavyO bjectInit =
    > heavyObjectInit Static; // line 14
    > return
    > heavyObject;
    > // line 15
    > }
    >
    > private synchronized HeavyObject getHeavyObjectI nternal(LazyIni t
    > lazyInit) // line 18
    > {
    > if(lazyInit.hea vyObject ==
    > null) // line 20
    > {
    > lazyInit.heavyO bject = new
    > HeavyObject(); // line 22
    > }
    > return
    > lazyInit.heavyO bject;
    > // line 24
    > }
    >
    > }
    >
    > static class
    > HeavyObjectInit
    > // line 29
    > {
    > public HeavyObject getHeavyObject( LazyInit
    > lazyInit) // line 31
    > {
    > return
    > lazyInit.heavyO bject;
    > // line 33
    > }
    > }
    >
    > public HeavyObject
    > getHeavyObject( )
    > // line 37
    > {
    > return
    > heavyObjectInit .getHeavyObject (this);
    > // line 39
    > }
    > }
    >
    > --------------------------------------------------------------------------[/color]
    -[color=blue]
    >
    > Explanation:
    > Lets assume this code is executing in a multi threading environment. Now
    > lets say a few threads enter LazyInit.getHea vyObject() at line 37
    > simultaneously. These threads will reach
    > heavyObjectInit .getHeavyObject () at line 39. Since heavyObjectInit is
    > initialized to HeavyObjectInit Sync (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 getHeavyObjectI nternal
    > 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
    > getHeavyObjectI nternal() 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 HeavyObjectInit Sync nonstatic while instance of
    > HeavyObjectInit static?
    > If the HeavyObjectInit Sync 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
    > HeavyObjectInit Sync, 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 HeavyObjectInit Sync 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 /[/color]


    Comment

    • David

      #3
      Re: Solving Lazy Initialization and double checked locking problem

      On Fri, 28 Nov 2003 18:32:23 +0000, Vinay Aggarwal wrote:
      [color=blue]
      >
      > 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/mem...edLocking.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.
      >[/color]

      I know this thread is getting old but I had an interview with someone who
      claims to have solved the DCL paradox. When I look at this I cannot help
      but think that if it were that simple many people would have solved it by
      now.> Here is the original problem. We need to lazy initialize a non static[color=blue]
      > 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(th is)
      > {
      > if (heavyObject == null)
      > {
      > heavyObject = new HeavyObject();
      > }
      > }
      > }
      > return
      > heavyObject;
      > }
      > }
      >
      > ---------------------------------------------------------------------------
      >
      > Unfortunately, this code is broken as described at
      > http://www.cs.umd.edu/~pugh/java/mem...edLocking.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 heavyObjectInit Static = new
      > HeavyObjectInit (); // line 5
      > private HeavyObjectInit heavyObjectInit = new
      > HeavyObjectInit Sync(); // line 6
      > private volatile HeavyObject heavyObject =
      > null; // line 7
      >
      > static class HeavyObjectInit Sync extends
      > HeavyObjectInit // line 9
      > {
      > public HeavyObject getHeavyObject( LazyInit
      > lazyInit) // line 11
      > {
      > HeavyObject heavyObject =
      > getHeavyObjectI nternal(lazyIni t); // line 13
      > lazyInit.heavyO bjectInit =
      > heavyObjectInit Static; // line 14
      > return
      > heavyObject;
      > // line 15
      > }
      >
      > private synchronized HeavyObject getHeavyObjectI nternal(LazyIni t
      > lazyInit) // line 18
      > {
      > if(lazyInit.hea vyObject ==
      > null) // line 20
      > {
      > lazyInit.heavyO bject = new
      > HeavyObject(); // line 22
      > }
      > return
      > lazyInit.heavyO bject;
      > // line 24
      > }
      >
      > }
      >
      > static class
      > HeavyObjectInit
      > // line 29
      > {
      > public HeavyObject getHeavyObject( LazyInit
      > lazyInit) // line 31
      > {
      > return
      > lazyInit.heavyO bject;
      > // 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.getHea vyObject() at line 37
      > simultaneously. These threads will reach
      > heavyObjectInit .getHeavyObject () at line 39. Since heavyObjectInit is
      > initialized to HeavyObjectInit Sync (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 getHeavyObjectI nternal
      > 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
      > getHeavyObjectI nternal() 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 HeavyObjectInit Sync nonstatic while instance of
      > HeavyObjectInit static?
      > If the HeavyObjectInit Sync 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
      > HeavyObjectInit Sync, 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 HeavyObjectInit Sync 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 /[/color]


      public class Singleton {
      private static Singleton _instance;

      public Singleton getInstance() {
      if(_instance == null ) {
      _instance = createInstance( );
      }

      return _instance;
      }

      private synchronized Singleton createInstance( ) {
      if(_instance == null) {
      return new Singleton;
      }
      }
      }

      Comment

      Working...