A Variant: The Double-Checked Locking Pattern

This pattern only applies to multithreaded applications. If you are not involved with multithreaded applications you might want to skip this section. This section assumes that you have a basic understanding of multithreaded issues, including synchronization.

A problem with the Singleton pattern may arise in multithreaded applications.

Suppose two calls to getInstance() are made at exactly the same time. This can be very bad. Consider what can happen in this case:

  1. The first thread checks to see if the instance exists. It does not, so it goes into the part of the code that will create the first instance.

  2. However, before it has done that, suppose a second thread also looks to see if the instance member is NULL. Since the first thread hasn't created anything yet, the instance is still equal to NULL, so the second thread also goes into the code that will create an object.

  3. Both threads now perform a new on the Singleton object, thereby creating two objects.

Is this a problem? It may or may not be.

  • If the Singleton is absolutely stateless, this may not be a problem.

  • In Java, the problem will simply be that we are taking up an extra bit of memory.

  • In C++, the program may create a memory leak, since it will only delete one of the objects when I have created two of them.

  • If the Singleton object has some state, subtle errors can creep in. For example,

    - If the object creates a connection, there will actually be two connections (one for each object).

    - If a counter is used, there will be two counters.

It may be very difficult to find these problems. First of all, the dual creation is very intermittent—it usually won't happen. Second, it may not be obvious why the counts are off, as only one client object will contain one of the Singleton objects while all of the other client objects will refer to the other Singleton.

At first, it appears that all I need to do is synchronize the test for whether the Singleton object has been created. The only problem is that this synchronization may end up being a severe bottleneck, because all of the threads will have to wait for the check on whether the object already exists.

Perhaps instead, I could put some synchronization code in after the if (instance== null) test. This will not work either. Since it would be possible that both calls could meet the NULL test and then attempt to synchronize, I could still end up making two Singleton objects, making them one at a time.

The solution is to do a “synch” after the test for NULL and then check again to make sure the instance member has not yet been created. I show this in Example 16-2. This is called double-checked locking.[3] The intent is to optimize away unnecessary locking. This synchronization check happens at most one time, so it will not be a bottleneck.

[3] Martin, R., Riehle, D., Buschmann, F., Pattern Language of Program Design, Reading, Mass.: Addison-Wesley, 1998, p. 363.

The features of double-checked locking are as follows:

  • Unnecessary locking is avoided by wrapping the call to new with another conditional test.

  • Support for multithreaded environments.

Example 16-2. Java Code Fragment: Instantiation Only
class USTax extends CalcTax {
    private static USTax instance;
    private USTax() {  }

    private synchronized static void doSync() {
        if (instance == null){
            instance = new USTax();
        }
    }

    public static USTax getInstance() {
        if (instance == null){
            doSync();
        }
        return instance;
    }
}

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.190.253.222