Solution of Single Case Model in Concurrent

The most memory-intensive object creation process must be constrained. As a Singleton, it always maintains that there is only one instance in the application, which can significantly improve the performance of the program.

The following four implementations of singleton are discussed.
 The stability of Singleton under single thread is excellent, which can be divided into two categories: 

1.Eager: Create objects immediately when classes are loaded.

public class EagerSingleton {
    //1. Instance objects are generated immediately when classes are loaded, and are acquired by the outside world by setting static variables.
    //2. Ensure package security by using private
    private static EagerSingleton eagerSingleton  = new EagerSingleton();
    
    //3. By privatizing the construction method, it is not allowed to create objects directly from outside to ensure the security of singletons.
    private EagerSingleton(){
    }
    public static EagerSingleton getEagerSingleton(){
        return eagerSingleton;
    }

2.Lazy (lazy): The object is not created immediately when the class is loaded, and is not instantiated until the first user gets it.

public class LazySingleton {
    //1. Class loading does not create a unique instance
    private static LazySingleton lazySingleton;
    
    private LazySingleton() {
    }
        
    //2. Provide a static method for obtaining instances
    public static LazySingleton getLazySingleton() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        } 
        return lazySingleton;
    }

In terms of performance, Lazy Singleton is significantly better than Eager Singleton. If class loading requires a lot of resources (e.g. to read large file information), the advantages of Lazy Singleton are obvious. But by reading the code, it's easy to find a fatal problem. How to maintain security between threads?

The following is an analysis of multithreaded concurrency:

The key to solve this problem lies in two aspects: 1. synchronization; 2. performance;
1. First, we solve the synchronization problem: Why does synchronization abnormality occur? Take a classic example as an explanation:
         
   Thread A, Thread B calls getLazy Singleton () to get an instance at the same time. When A calls, it judges instance to be null. When it is ready for initialization, suddenly thread A hangs up. At this time, the object is not instantiated successfully. Worse thing happens later. Thread B runs. He also judges instance to be null. At this time, both A and B enter the instantiating stage, which results in two instances. Destroy the principle of singleton.        

How to save it?
As a java developer, synchronized must be familiar with it. When it comes to multithreading, most people think of him (after JDK6, his performance has improved enormously, solving simple concurrency is very useful).

Let's try to solve it with synchronized.
//synchronized Locking
public synchronized static LazySingleton getLazySingleton() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        } 
        return lazySingleton;
    }

Such synchronization seems to be a solution, but as a developer, the most important thing is performance protection. There are advantages and disadvantages of using synchronized. Because of the lock operation, code segments are added pessimistic lock. Only when one request is completed, the next request can be executed. Usually, code chips with synchronized keywords are several times slower than code of the same magnitude, which we don't want to see. How to avoid this problem? There is a suggestion in java's definition of synchronized: the later synchronized is used, the better performance (fine locking).

###### 2. Therefore, we need to start solving performance problems. According to synchronized optimization:######

public class DoubleCheckLockSingleton {
    //Using volatile ensures that each value is fetched not from the cache, but from the real memory address. (explained below)
    private static volatile DoubleCheckLockSingleton doubleCheckLockSingleton;
    
    private DoubleCheckLockSingleton(){
        
    }
    
    public static DoubleCheckLockSingleton getDoubleCheckLockSingleton(){
        //Configure double-check locks (explained below)
        if(doubleCheckLockSingleton == null){
            synchronized (DoubleCheckLockSingleton.class) {
                if(doubleCheckLockSingleton == null){
                    doubleCheckLockSingleton = new DoubleCheckLockSingleton();
                }
            }
        }
        return doubleCheckLockSingleton;
    }
}

The above source code is the classic volatile keyword (JDK 1.5 rebirth) +double check lock (Double Check), which optimizes the performance overhead brought by sychronized to the greatest extent. The following will explain volatile and DoubleCheck.

1.volatile

It was only after JDK 1.5 that it was officially implemented. Previous versions only defined the keyword, but did not implement it specifically. If you want to understand volatile, you must have some knowledge of JVM's own memory management:

1.1 Following Moore's law, the speed of memory reading and writing is far from satisfying CPU, so modern computers introduce the mechanism of adding cache to CPU. The cache prereads the value of memory and stores it temporarily in the cache. By calculating, the corresponding value in memory is updated.

** 1.2** and JVM imitates this method of PC, dividing its own ** working memory ** in memory. This part of memory function is consistent with cache, which significantly improves the efficiency of JVM, but there are advantages and disadvantages in everything. This practice also leads to transmission problems when working memory communicates with other memory. One function of volatile is to force the latest values to be read from memory to avoid inconsistencies between cache and memory.

1.3 Another function of volatile is related to JVM, that is, JVM will rearrange the execution sequence of source code through its own judgment, to ensure the instruction pipeline coherence, in order to achieve the optimal execution scheme. This approach improves performance, but produces unexpected results for DoubleCheck, where the two threads may interfere with each other. Volatile provides happens-before guarantee (writing takes precedence over reading), which keeps objects free from interference and ensures security and stability.

2.DoubleCheck

This is a legacy of modern programming. Assuming that the object has been instantiated after entering the synchronization block, it needs to be judged again.

There is, of course, an officially recommended single implementation method:

Since the construction of classes is atomic in the definition, all the above problems will not arise again. It is a good way to realize the singleton and is recommended to use.

//Singleton construction using internal classes
public class NestedClassSingleton {
    private NestedClassSingleton(){
        
    }
    private static class SingletonHolder{
        private static final NestedClassSingleton nestedClassSingleton = new NestedClassSingleton();
    }
    public static NestedClassSingleton getNestedClassSingleton(){
        return SingletonHolder.nestedClassSingleton;
    }
}

Zhu an an
ooooor

Keywords: Java jvm JDK Programming

Added by jshowe on Fri, 17 May 2019 16:22:44 +0300