The conclusion of using DCL is to avoid DCL

Source: Internet
Author: User

 

 

I. The problem of using DCL for delayed loading of non-static domains: the compiler will write unordered

1. What is unordered write: Initialize first and then point. The actual situation is that the write point is initiated.
When instance = new Singleton (); is executed, the following pseudo code is executed:
Mem = allocate (); // allocate memory for singleton object.
Instance = MEM; // note that instance is now non-null,
// Has not been initialized.
Ctorsingleton (instance); // invoke constructor for Singleton passing
// Instance.
This pseudocode is not only possible, but also occurs in some JIT compilers. The execution order is reversed, but this is allowed in view of the current memory model. This line of JIT compiler is only an academic practice to lock the dual check.

2. Optimization Result: When a domain of the constructor instance is called, The status is not null but not fully initialized. The correct status should be either null or non-null and fully initialized. This result will give programmers the potential problems of correct DCL.

II. The evolution of delayed loading using DCL for non-static domains:

1. Programmers think that using intermediate variables can solve unordered writing:

public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
Singleton inst = instance; //2
if (inst == null)
{
synchronized(Singleton.class) { //3
inst = new Singleton(); //4
}
instance = inst; //5
}
}
}
return instance;
}

This code tries to avoid unordered writing. It tries to solve this problem by introducing the local variable inst and the second synchronized block. This theory is implemented as follows:
1. Thread 1 enters the getinstance () method. Bytes
2. Because instance is null, thread 1 enters the first synchronized block at // 1. Bytes
3. Obtain the Instance value from the local variable Inst. The value is null at // 2. Bytes
4. Because inst is null, thread 1 enters the second synchronized block at // 3. Bytes
5. Thread 1 then starts executing code at // 4, And inst is not null, but before the singleton constructor is executed. (This is the unordered write problem we just saw .) Bytes
6. Thread 1 is pre-occupied by thread 2. Bytes
7. Thread 2 enters the getinstance () method. Bytes
8. Because instance is null, thread 2 tries to enter the first synchronized block at // 1. Because thread 1 currently holds this lock, thread 2 is blocked. Bytes
9. Thread 1 is then executed at // 4. Bytes
10. Thread 1 then assigns a fully constructed singleton object to the variable instance at // 5 and exits the two synchronized blocks. Bytes
11. Thread 1 returns instance. Bytes
12. Then run thread 2 and assign the Instance value to inst at // 2. Bytes
13. If thread 2 finds that the instance is not null, return it.
The key line here is // 5. This line should ensure that the instance is null only or reference a fully constructed singleton object. This problem occurs when the theory and practice are different from each other.
The code in listing 7 is invalid due to the definition of the current memory model. Java language specification (JLS) requires that the code in the synchronized block cannot be removed. However, it is not said that the code outside the synchronized block cannot be moved into the synchronized block.
The JIT compiler will see an opportunity for optimization here. This optimization will delete the code at // 4 and // 5 and combine and generate the following code:

public static Singleton getInstance()
{
if (instance == null)
{
synchronized(Singleton.class) { //1
Singleton inst = instance; //2
if (inst == null)
{
synchronized(Singleton.class) { //3
//inst = new Singleton(); //4
instance = new Singleton();
}
//instance = inst; //5
}
}
}
return instance;
}

Due to compiler optimization, unordered writing still exists.

Private volatile fieldtype field; // note that volatile must be used. In addition to visibility, volatile also has a function to avoid Compiler optimization.
Fieldtype getfield (){
Fieldtype result = field;

If (result = NULL) {// first check (no locking)
Synchronized (this) {// This will not be like the entire Synchronized Method in 1). As a result, after successful initialization, access to the getinstance will still be inefficient.
Result = field; // The reason why result is used here is that even if the result is not null due to JIT unordered write and is not fully initialized, the field can be either null, or it is fully initialized. Field is null.
If (result = NULL) {// second check (with locking) Field
// The field must be assigned to the field after the result is fully initialized.
Field = Result = computefieldvalue ();
}
}
Return result;
}

2. The last method to use DCl and to avoid Compiler optimization is to use volatile to modify the domain to avoid Compiler optimization.

The following example is from objective java. The implementation of DCL uses volatile, intermediate variable, and internal locking. Very troublesome.

3. Conclusion of DCL: recommended methods for delayed loading in non-static domains: 1. Use delayed loading instead of DCL and roll back to the synchronized method with a loss of performance. 2. Use delayed loading, use a static field to modify, and use static holder to delay loading. 3. initialize immediately without using delayed loading.

4. Amazing volatile
In addition to visibility, volatile also has an important function to avoid compiler tuning.

1. Volatile is not used:

class Test{
private boolean stop = false;
private int num = 0;

public void foo() {
num = 100;
stop = true;
//...
}

public void bar() {
if (stop)
num += num; //num=0+0,num can be 0
}
//...
}

As mentioned in jsr133, generally, JVM will optimize programs that it deems will not affect the context. One of the optimizations is re-order. That is to say, in the above Foo function, the execution sequence of the two statements may be exchanged. This is why num can be 0 in the preceding comment. What does this have to do with volatile? In fact, if the above two variables are added with the volatile modifier, the above re-order will not happen.
Generally, JVM performs proper optimization on programs that it deems will not affect the context. One of the optimizations is re-order. That is to say, in the above Foo function, the execution sequence of the two statements may be exchanged.

2. When volatile is used, the purpose is to avoid the re-order compiler.

class test
{
private volatile boolean stop = false;
private volatile int num = 0;

public void foo()
{
num = 100; //This can happen second
stop = true; //This can happen first
//...
}

public void bar()
{
if (stop)
num += num; //num can == 0!
}
//...
}

3. Related Articles do not mention volatile as the final solution to DCl. However, objective JAVA does this.

 

 

Parameter http://www.ibm.com/developerworks/cn/java/j-dcl.html

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.