Double-checked locking
   HOME

TheInfoList



OR:

In
software engineering Software engineering is a systematic engineering approach to software development. A software engineer is a person who applies the principles of software engineering to design, develop, maintain, test, and evaluate computer software. The term '' ...
, double-checked locking (also known as "double-checked locking optimization") is a
software design pattern In software engineering, a software design pattern is a general, reusable solution to a commonly occurring problem within a given context in software design. It is not a finished design that can be transformed directly into source or machine co ...
used to reduce the overhead of acquiring a
lock Lock(s) may refer to: Common meanings *Lock and key, a mechanical device used to secure items of importance *Lock (water navigation), a device for boats to transit between different levels of water, as in a canal Arts and entertainment * ''Lock ...
by testing the locking criterion (the "lock hint") before acquiring the lock. Locking occurs only if the locking criterion check indicates that locking is required. The pattern, when implemented in some language/hardware combinations, can be unsafe. At times, it can be considered an
anti-pattern An anti-pattern in software engineering, project management, and business processes is a common response to a recurring problem that is usually ineffective and risks being highly counterproductive. The term, coined in 1995 by computer programmer An ...
.David Bacon et al
The "Double-Checked Locking is Broken" Declaration
It is typically used to reduce locking overhead when implementing "
lazy initialization In computer programming, lazy initialization is the tactic of delaying the creation of an object, the calculation of a value, or some other expensive process until the first time it is needed. It is a kind of lazy evaluation that refers specificall ...
" in a multi-threaded environment, especially as part of the
Singleton pattern In software engineering, the singleton pattern is a software design pattern that restricts the instantiation of a class to a singular instance. One of the well-known "Gang of Four" design patterns, which describe how to solve recurring problems ...
. Lazy initialization avoids initializing a value until the first time it is accessed.


Usage in C++11

For the singleton pattern, double-checked locking is not needed: Singleton& GetInstance() C++11 and beyond also provide a built-in double-checked locking pattern in the form of std::once_flag and std::call_once: #include #include // Since C++17 // Singleton.h class Singleton ; // Singleton.cpp std::optional Singleton::s_instance; std::once_flag Singleton::s_flag; Singleton* Singleton::GetInstance() If one truly wishes to use the double-checked idiom instead of the trivially working example above (for instance because Visual Studio before the 2015 release did not implement the C++11 standard's language about concurrent initialization quoted above ), one needs to use acquire and release fences: #include #include class Singleton ; Singleton* Singleton::GetInstance()


Usage in Go

package main import "sync" var arrOnce sync.Once var arr []int // getArr retrieves arr, lazily initializing on first call. Double-checked // locking is implemented with the sync.Once library function. The first // goroutine to win the race to call Do() will initialize the array, while // others will block until Do() has completed. After Do has run, only a // single atomic comparison will be required to get the array. func getArr() []int func main()


Usage in Java

Consider, for example, this code segment in the Java (programming language), Java programming language as given by (as well as all other Java code segments): // Single-threaded version class Foo The problem is that this does not work when using multiple threads. A
lock Lock(s) may refer to: Common meanings *Lock and key, a mechanical device used to secure items of importance *Lock (water navigation), a device for boats to transit between different levels of water, as in a canal Arts and entertainment * ''Lock ...
must be obtained in case two threads call getHelper() simultaneously. Otherwise, either they may both try to create the object at the same time, or one may wind up getting a reference to an incompletely initialized object. The lock is obtained by expensive synchronizing, as is shown in the following example. // Correct but possibly expensive multithreaded version class Foo However, the first call to getHelper() will create the object and only the few threads trying to access it during that time need to be synchronized; after that all calls just get a reference to the member variable. Since synchronizing a method could in some extreme cases decrease performance by a factor of 100 or higher, the overhead of acquiring and releasing a lock every time this method is called seems unnecessary: once the initialization has been completed, acquiring and releasing the locks would appear unnecessary. Many programmers have attempted to optimize this situation in the following manner: # Check that the variable is initialized (without obtaining the lock). If it is initialized, return it immediately. # Obtain the lock. # Double-check whether the variable has already been initialized: if another thread acquired the lock first, it may have already done the initialization. If so, return the initialized variable. # Otherwise, initialize and return the variable. // Broken multithreaded version // "Double-Checked Locking" idiom class Foo Intuitively, this algorithm is an efficient solution to the problem if the runtime has a fence primitive (which manages memory visibility across execution units), otherwise the algorithm should be avoided. For example, consider the following sequence of events: # Thread ''A'' notices that the value is not initialized, so it obtains the lock and begins to initialize the value. # Due to the semantics of some programming languages, the code generated by the compiler is allowed to update the shared variable to point to a partially constructed object before ''A'' has finished performing the initialization. For example, in Java if a call to a constructor has been inlined then the shared variable may immediately be updated once the storage has been allocated but before the inlined constructor initializes the object. # Thread ''B'' notices that the shared variable has been initialized (or so it appears), and returns its value. Because thread ''B'' believes the value is already initialized, it does not acquire the lock. If ''B'' uses the object before all of the initialization done by ''A'' is seen by ''B'' (either because ''A'' has not finished initializing it or because some of the initialized values in the object have not yet percolated to the memory ''B'' uses ( cache coherence)), the program will likely crash. One of the dangers of using double-checked locking in J2SE 1.4 (and earlier versions) is that it will often appear to work: it is not easy to distinguish between a correct
implementation Implementation is the realization of an application, or execution of a plan, idea, model, design, specification, standard, algorithm, or policy. Industry-specific definitions Computer science In computer science, an implementation is a real ...
of the technique and one that has subtle problems. Depending on the
compiler In computing, a compiler is a computer program that translates computer code written in one programming language (the ''source'' language) into another language (the ''target'' language). The name "compiler" is primarily used for programs tha ...
, the interleaving of threads by the
scheduler A schedule or a timetable, as a basic time-management tool, consists of a list of times at which possible tasks, events, or actions are intended to take place, or of a sequence of events in the chronological order in which such things are i ...
and the nature of other concurrent system activity, failures resulting from an incorrect implementation of double-checked locking may only occur intermittently. Reproducing the failures can be difficult. As of
J2SE 5.0 The Java (programming language), Java language has undergone several changes since Java Development Kit, JDK 1.0 as well as numerous additions of class (computer science), classes and packages to the standard library (computer science), li ...
, this problem has been fixed. The volatile keyword now ensures that multiple threads handle the singleton instance correctly. This new idiom is described i

an

// Works with acquire/release semantics for volatile in Java 1.5 and later // Broken under Java 1.4 and earlier semantics for volatile class Foo Note the local variable "", which seems unnecessary. The effect of this is that in cases where is already initialized (i.e., most of the time), the volatile field is only accessed once (due to "" instead of ""), which can improve the method's overall performance by as much as 40 percent. Java 9 introduced the class, which allows use of relaxed atomics to access fields, giving somewhat faster reads on machines with weak memory models, at the cost of more difficult mechanics and loss of sequential consistency (field accesses no longer participate in the synchronization order, the global order of accesses to volatile fields). // Works with acquire/release semantics for VarHandles introduced in Java 9 class Foo If the helper object is static (one per class loader), an alternative is the initialization-on-demand holder idiom (See Listing 16.6 from the previously cited text.) // Correct lazy initialization in Java class Foo This relies on the fact that nested classes are not loaded until they are referenced. Semantics of field in Java 5 can be employed to safely publish the helper object without using : public class FinalWrapper public class Foo The local variable is required for correctness: simply using for both null checks and the return statement could fail due to read reordering allowed under the Java Memory Model. Performance of this implementation is not necessarily better than the implementation.


Usage in C#

Double-checked locking can be implemented efficiently in .NET. A common usage pattern is to add double-checked locking to Singleton implementations: public class MySingleton In this example, the "lock hint" is the _mySingleton object which is no longer null when fully constructed and ready for use. In .NET Framework 4.0, the Lazy<T> class was introduced, which internally uses double-checked locking by default (ExecutionAndPublication mode) to store either the exception that was thrown during construction, or the result of the function that was passed to Lazy<T>: public class MySingleton


See also

* The
Test and Test-and-set In computer science, the test-and-set CPU instruction is used to implement mutual exclusion in multiprocessor environments. Although a correct lock can be implemented with test-and-set, it can lead to resource contention in busy lock (caused b ...
idiom for a low-level locking mechanism. * Initialization-on-demand holder idiom for a thread-safe replacement in Java.


References


External links

* Issues with the double checked locking mechanism captured i
Jeu George's Blogs
* "Double Checked Locking" Description from the Portland Pattern Repository * "Double Checked Locking is Broken" Description from the Portland Pattern Repository * Paper
C++ and the Perils of Double-Checked Locking
(475 KB) by
Scott Meyers Scott Douglas Meyers (born April 9, 1959) is an American author and software consultant, specializing in the C++ computer programming language. He is known for his ''Effective C++'' book series. During his career, he was a frequent speaker at co ...
and Andrei Alexandrescu * Article
Double-checked locking: Clever, but broken
by
Brian Goetz Brian (sometimes spelled Bryan in English) is a male given name of Irish and Breton origin, as well as a surname of Occitan origin. It is common in the English-speaking world. It is possible that the name is derived from an Old Celtic word mea ...
* Article
Warning! Threading in a multiprocessor world
by Allen Holub
Double-checked locking and the Singleton pattern



volatile keyword in VC++ 2005

Java Examples and timing of double check locking solutions
* {{Design Patterns patterns Concurrency control Software design patterns Articles with example Java code