1. The scene:

Interviewer: Have you ever used singleton patterns?

Me: Yes, yes.

Interviewer: How many ways can you write singleton?

Me: lazy and hungry, lazy and hungry.

Interviewer: Well, we all know that synchronized locking is expensive, and your method of obtaining a lock every time you access it (basic lazy) is inefficient. Is there a way to optimize it?

Me: meditate for a moment, a flash of light in my mind. You can double check the lock. Blah, blah, blah. (I’m glad I’ve seen it before.)

Interviewer: Why does double-checking lock require volatile?

Me: Why don’t we ask Baidu?

Before answering this question, we need to clarify a few points: object creation, instruction reordering, the concept of CPU time slices, synchronization that does not prohibit instruction reordering, and volatile that prohibits instruction reordering.

2. Object creation process

The object creation process is mainly divided into three steps, as shown in the assembly code shown below, mainly 0,4, and 7.

The 0 step allocates memory for the newly created object, but the value of the member variable in the object is the default value (semi-initialized), which is 0 in figure A.

4 initializes the object and assigns 10 to the member variable A only at this step

7 Associate the testDemo reference with the testDemo object generated from new


public class TestDemo {
    private int a = 10;
    public static void main(String[] args) {
 
        TestDemo testDemo = new TestDemo();
//        0 new # 2 < company/syncronized TestDemo > application memory, half initialization, at this time a value is 0 (when object just came out of the new member variables in a to set the default initial value of the int type of initial value is zero)// 3 DUP copy // 4 Invokespecial# 3 < company/syncronized TestDemo. < init > > initialization, 10 in this step is assigned to a, when a value is 10
//        7 astore_1                                 testAssociating Demo with new TestDemo() // 8return}}Copy the code

3. Order rearrangement

Instruction reordering is a concept in the JMM(Java Memory Model) in which the compiler and processor reorder instructions that do not have data dependencies when a computer executes a program.

For example, if statement 2 is used only after statement 1 declares variable A, then there is A data dependency between them.

What is an instruction rearrangement: normally, the rely() method runs in 1,2,3,4 order, but the compiler and processor optimizations can change the order to 1,3,2,4, or 3,1,2,4, which is a rearrangement of instructions. Is it possible to have 1,4,2,3? No, there is a dependency on 3,4, so 3 has to be executed before 4.

public void rely(){ int a = 10; // statement 1 a = a +1; // statement 2 int b = 8; // statement 3 b = b +1; // statement 4} '### 4.CPU execution time sliceTime slice definition: time slice refers to the time allocated by the CPU to each program. Each thread is allocated a time slice, called its time slice, that is, the time allowed to run by the process, so that each program appears to be simultaneously. If the process is still running at the end of the time slice, the CPU is stripped and allocated to another process. If the process blocks or ends before the time slice ends, the CPU switches immediately. Without wasting CPU resources. > > problems: according to the above definition of time slice, we can conclude that the CPU will not wait for after a process has been completed in the execution of a process, but the CPU to each process will be a period of time, the end of the period of time, will turn to the next process to use the CPU, so that there will be a question of the above? If my process is running in the middle of the time slice, the CPU is given to the next process. Then my process is running in the middle of the time slice, and I need to wait for the next time to occupy the CPU to complete the execution of all processes. That is, a process can take more than one CPU elapsed time slice to complete.5. Effect of instruction rearrangement on double-checked locking mode> < p style = "max-width: 100%; clear: both; min-height: 1em; After the analysis, we know that creating an object is a three-step process, as shown in figure 1,2,3. Is it possible that 2,3 instructions will be reordered during the creation process? Synchronized does not prohibit instruction rearrangement, assuming that 2,3 instructions are exchanged in sequence, i.etestThe reference first establishes a connection with the New SingleDemo object and then initializes the new SingleDemo, where the problem arises. < span style = "font-size: 14px; line-height: 20pxtestThe reference establishes the connection, the CPU time slice runs out, and the next process executes. Synchronized guarantees atomicity. My thread hasn't finished executing, I'm still holding the lock, and if the next thread comes in, it's blocked, it doesn't affect me. It's only when I get the CPU next, and I finish, and I release the lock, that the next thread can do something, so since the current thread is going to finish, it doesn't really matter. Yes, if my current thread completes, it really doesn't matter, but what if my next process comes in and doesn't block? > > < span style = "font-size: 10.5pttestThe reference refers to an uninitialized, half-initialized new Singledemo object, that is, at this pointtestIt's not empty, and then I run out of CPU time slice, and then the current thread A gives up the CPU, but the current thread A still holds the lock. I'm going to come in next thread B, in the outer layerif(test==null) and then find metestObject has something in it, and then it just goesreturnThread B has obtained an initialized new Singledemo object. If thread A locks the new Singledemo object, thread B has obtained the new Singledemo object. So what happens is that the member variable information in thread B is wrong. If I have a=10 and you get a=0 for the semi-initialized object, then I'm not using the member variable correctly. > > So how to solve the instruction reorder problem? Add the volatitle keyword to ensure that no command reordering occurs during new Singledomo.Copy the code

public class SingleDemo { //private volatile static SingleDemo test =null;

private static SingleDemo test =null; private int a = 10; public int getA() { return a; Private SingleDemo(){} public static SingleDemo get2(){if(test==null) (SingleDemo.class) { if (test == null) { test = new SingleDemo(); //new singleDemo object, semi-initialize //1 // initialize //2 //test reference and new singleDemo object connection //3}}} return test; }Copy the code

}

In fact, for Java programmers, I also prepared a free Java interview materials (including high availability, high concurrency, high performance and distributed, Jvm performance tuning, MyBatis, Netty, Redis, Kafka, Mysql, Zookeeper, Tomcat, Docker, Dubbo, Nginx interview questions, such as multiple knowledge companies) is similar to the related PDF is free to share, hope to be able to help to a friend in need, At the same time, it also saves us time to find information on the Internet. For more information, please leave a message or send a private message in the comments section below: After that, xiaobian will continue to share the real interview cases I met in other big factories. Pay attention to my car and don't lose!Copy the code