Singleton mode and volatile

Normal singleton pattern

Hungry Chinese style: Using the static keyword, static methods are called when the class is initialized 

public class Singleton {
    private static  final Singleton singleton=new Singleton();
    private Singleton(){

    }
    public static Singleton getInstance(){
        return singleton;
    }
}

Disadvantages: This object may not be used at this time, wasting resources (reference: class initialization timing )

Singleton mode optimization

Lazy: declared as null, it is initialized when it is used, but it needs to be locked to prevent concurrency of threads, two objects are generated

public class Singleton {
    private static   Singleton singleton=null;
    private Singleton(){

    }
    public synchronized static Singleton getInstance(){
        if (singleton==null){
            System.out.println("我的其他业务");
            singleton=new Singleton();
        }
        return singleton;
    }
}

Disadvantages: Some business codes in the method that do not need to be locked are also locked, and the granularity of the lock is coarse

Deep optimization (refine the lock)

Need to add two locks when refining the lock

public class Singleton {
    private static   Singleton singleton=null;
    private Singleton(){

    }
    public  static Singleton getInstance(){
        if (singleton==null){
            System.out.println("我的其他业务");
            synchronized (Singleton.class){//Double Check Lock
                if (singleton==null){
                    singleton=new Singleton();
                }
            }
        }
        return singleton;
    }
}

The reason why DDL adds two locks: prevents multiple threads from executing business logic at the same time

Singleton final version: Security due to CPU instruction rearrangement

Because the CPU may order rearrangement: when declaring variables, you need to add the volatile keyword

    private static  volatile Singleton singleton=null;

The following explains why the volatile keyword is added

In our idea idea-view-Show Bytecode we can see the bytecode of our method 

such as

 Object o = new Object();

Bytecode

    NEW java/lang/Object
    DUP
    INVOKESPECIAL java/lang/Object.<init> ()V
    ASTORE 1

When we create a new object, there are basically three steps

1) Apply for an object in the heap memory to allocate a piece of memory, at this time the value in the object is a default value,

2) Then call the constructor to initialize,

3) Assign the reference of the heap memory to o o to execute the Object object in the heap memory and establish the association

Allocate memory-initialize-establish association

When a linear 1new, when you go to the first step of allocating memory, CPU instruction reordering occurs, first establish the association (at this time, the associated object value is empty because it has not been initialized),

This is when thread 2 comes in and finds that the object is not empty (because the association has been established in the third step). It is directly used and the object used at this time is a semi-finished product (because thread 1 has not been initialized)

The role of volatile has two points (the implementation principles of these two points will be updated next week ...)

1) Multi-thread direct visibility (the cache consistency protocol maintains data consistency in cache lines)

2) Prevent CPU instruction reordering (JVM specification requires a barrier to memory)

Why does the CPU reorder

If there are two instructions for the CPU to execute and there are no direct dependencies between the two instructions,

If you need to read data from the memory during the execution of the first instruction, you can execute the second instruction first , because the CPU's operation speed is 100 times faster than the memory's reading speed

Doing so can increase the overall operating efficiency of the calculator

For example, we can wash the dishes when we are boiling water. Although the water is boiled first, the action of washing the dishes may be performed first.

At this time, the second instruction may be executed before the first instruction. In the original execution, the order of CPU execution behind 1 2 may be 2 1 (because it is two instructions, this possibility occurs only in the case of concurrent ).

public static void main(String[] args) throws Exception{
        int i=0;
        for (;;){
            i++;
            x=0;y=0;
            a=0; b=0;
            Thread one = new Thread(() -> {
                 a=1;x=b;
            });
            Thread two = new Thread(() -> {
                b=1;y=a;
            });
            one.start();two.start();
            one.join(); two.join();
            String result="第"+i+"次执行 ("+x+" "+y+")";
            if (x==0&&y==0){
                System.out.println(result);
                break;
            }else{

            }
        }
    }
View Code

After the above code was run 214609 times, it printed that x = 0 y = 0, which proves that the CPU has instruction reordering

Guess you like

Origin www.cnblogs.com/ssskkk/p/12708783.html