Source: "The" double-checked locking is broken "declaration"
1. Simple implementation of a single case model
Only single-threaded version
class Foo {
private helper helper = null is supported;
Public helper Gethelper () {
if (helper = null)
helper = new Helper ();
return helper;
}
In the case of multithreading, multiple helper instances may be generated.
2. Synchronous Gethelper Method
Support for multithreaded version
class Foo {
private helper helper = null;
Public synchronized Helper Gethelper () {
if (helper = null)
helper = new Helper ();
return helper;
}
After the Gethelper () method is marked as synchronized, the JVM allows only one thread at a time to execute the Gethelper method. Therefore, multiple helper instances are not generated. However, the overhead of synchronization can result in lower multithreaded execution efficiency.
3. A wrong double check lock method
A faulty double check lock method
class Foo {
private helper helper = null;
Public helper Gethelper () {
if (helper = = null)
synchronized (this) {
if (helper = null)
helper = new Help ER ();
}
return helper;
}
Why is wrong.
Reason: in helper = new Helper (); There are two main operations in this sentence. One is to create an instance of the helper, and the second is to assign the reference of this newly created helper instance to the helper field. The compiler stipulates that the order of these two operations may be a reference to the first assignment instance, and then perform an initialization build inside the helper instance. This may cause another thread to invoke the Gethelper () method when the Because the helper field is not NULL, the instance that the helper refers to starts to work, and the initialization of the instance is still incomplete in the first thread, which causes the second to use a "bad" Helper.
Even if the compiler prescribes that an instance is assigned first, in a multiprocessor system, the processor and memory system may reverse the order of the two operations.
4. Another kind of wrong double check lock method
Another wrong double check lock method
class Foo {
private helper helper = Null;wei Public
Helper Gethelper () {
if (Helper = null ) {
Helper h;
Synchronized (this) {
h = helper;
if (h = = null)
synchronized (this) {
h = new Helper ();
} Free internal synchronized lock
helper = h;
}
}
return helper;
}
Why is wrong.
Reason: The rule for exit monitoring (MONITOREXIT), such as releasing sychronized locks, is "the operation before Monitorexit must be performed before the lock is released." However, there is no rule that "the operation after Monitorexit must be released before the lock can be performed." That is to say, the compiler may put helper = h; This sentence moves inside the synchronized code block. This goes back to the case in 3 where other threads might get a "bad", unfinished helper instance.
Note: The situation is different in the. Net CLR. In the CLR, the invocation of any lock method constitutes a complete memory fence, and any variables written before the fence must be completed before the fence, and any variable readings after the fence must begin after the fence.
The more synchronization is used, the more likely it is to cause performance problems and increase the likelihood of errors.
In addition, a backup of the variable values that each processor caches, and in some types of processors, even if the other processor uses the memory barrier (memory barriers) to write the new value to the shared memory, because the processor is using its own backup value, the helper value is considered null. Causes a new helper instance to be created and uses this new instance. (Alpha processor)
5. Can double check locks (such as int and float) for raw type data variables of 32 bits
Because the variables of the original type data are stored in the value itself, the variable content is directly changed by assigning a value to it. However, 64-bit primitive type data variables, such as long and double, do not guarantee the atomicity of the operation.
Class Foo {
private int cachedhashcode = 0;
public int hashcode () {
int h = cachedhashcode;
if (h = = 0)
synchronized (this) {
if (cachedhashcode!= 0) return cachedhashcode;
h = Computehashcode ();
Cachedhashcode = h;
}
return h;
}
}
6. Dual-Check-lock method using thread-local storage
Class Foo {
//if Perthreadinstance.get () returns a non-null value, indicating that the method has been executed by a thread, the helper has been initialized to the
private final ThreadLocal Perthreadinstance = new ThreadLocal ();
Private helper helper = null;
Public Helper Gethelper () {
if (perthreadinstance.get () = null) Createhelper ();
return helper;
}
Private final void Createhelper () {
synchronized (this) {
if (helper = null)
helper = new Helper ();
}
//Any Non-null value can be used as a parameter of set
Perthreadinstance.set (perthreadinstance);
}
The efficiency of this method depends on the implementation of threadlocal in the JDK.
7. Using volatile double check lock method
Starting with JDK5, volatile strictly restricts the read and write order of variables and does not allow rearrangement.
Class Foo {
private volatile helper helper = null;
Public helper Gethelper () {
if (helper = = null) {
synchronized (this) {
if (helper = null)
helper = new He Lper ();
}
return helper;
}
Reading and writing to immutable objects (immutable Objects) are atomic operations. So if the helper is immutable objects, such as String and Integer, you can use the volatile keyword.
8. Summary:
Try to use the best practice you have and don't invent it yourself.
A suitable programming language should be elegant. If you find that existing problems are difficult to solve in an elegant way, either change a language or make a sound, and let the person who develops the language improve the language from the source.
For most of you who just use the language, don't study it for the sake of language, you should study it based on existing business problems. More contact with a variety of business, natural will encounter a variety of problems, naturally have the opportunity to learn more.
C # single Case pattern collation