The single case model has five kinds of writing: lazy, a hungry man, double check lock, static internal class, enumeration. _ Design mode

Source: Internet
Author: User
Tags volatile

http://blog.csdn.net/nsw911439370/article/details/50456231


Turn https://biezhi.me/article/how-to-correctly-write-singleton-pattern.html

The single example pattern is the easiest to understand and the easiest way to write code in design mode. But there are a lot of pits, so also often as a face test questions. This paper mainly deals with several kinds of single cases, and analyzes their advantages and disadvantages. A lot of it is a cliché, but if you don't know how to create a single thread-safe case and don't know what a double check lock is, that article might help you. Lazy type, thread unsafe

When asked to implement a single case pattern, many people's first reaction was to write the following code, including what the textbook taught us.

public class Singleton {
    private static Singleton instance;
    Private Singleton () {} public

    static Singleton getinstance () {
     if (instance = = null) {
         instance = new Singleto n ();
     }
     return instance
    }
}

This code is straightforward and uses lazy load mode, but there are fatal problems. When multiple threads call getinstance () in parallel, multiple instances are created. In other words, it does not work under multithreading. Lazy, thread-safe

The easiest way to solve the above problem is to set the entire getinstance () method to sync (synchronized).

public static synchronized Singleton getinstance () {
    if (instance = = null) {
        instance = new Singleton ();
    } return
    instance;
}

Although it is thread-safe and solves multiple-instance problems, it is not efficient. Because only one thread can call the GetInstance () method at any time. However, the synchronization operation needs to be required only on the first call, that is, when a singleton instance object is first created. This leads to a double check lock. Double check Lock

Double-check lock mode (double checked locking pattern) is a method of using synchronized blocks to lock. The programmer calls it a double check lock because there will be two checks instance = NULL, once outside the sync block and once in the sync block. Why do I have to check again in the sync block? Because there may be more than one thread going into the sync block together, multiple instances will be generated if no two tests are performed within the synchronization block.

public static Singleton Getsingleton () {
    if (instance = null) {                         //single Checked
        synchronized ( Singleton.class) {
            if (instance = = null) {                 //double Checked
                instance = new Singleton ()
            ;
    }}} return
    instance;
}

This code looks perfect, unfortunately, it is problematic. The main reason is that instance = new Singleton (), this is not an atomic operation, in fact in the JVM this sentence probably did the following 3 things. Allocating memory to the instance call Singleton constructor to initialize the member variable to point the instance object to the allocated memory space (instance is not NULL after this step)

However, there is an optimization of instruction reordering in the JVM's Just-in-time compiler. In other words, the order of the second and third steps above is not guaranteed, and the final order of execution may be 1-2-3 or 1-3-2. If it is the latter, then the 3 execution, 2 is not executed before the thread two preemption, then instance has been non-null (but not initialized), so thread two will return directly to instance, and then use, and then logically to the error.

We just need to declare the instance variable as volatile.

public class Singleton {
    Private volatile static Singleton instance;//declared as volatile
    private Singleton () {}

    PU Blic static Singleton Getsingleton () {
        if (instance = = null) {                         
            synchronized (singleton.class) {
                if (instance = = null) {       
                    instance = new Singleton ();
        }} return instance
    }
   
}

Some people think that the reason for using volatile is visibility, which is to ensure that the thread does not hold a copy of the instance locally, each time it is read in the main memory. But it's not right, actually. The main reason for using volatile is another feature: banning command reordering optimizations. That is, there is a memory barrier (on the generated assembler code) behind the assignment operation of the volatile variable, and the read operation is not reordered before the memory barrier. For example, the above example, the fetch operation must be done after 1-2-3 or after the 1-3-2, there is no execution to 1-3 and then fetch the value of the case. From the point of view of "first occurrence principle", it is that the write operation for a volatile variable occurs first in the face of the variable reading ("Back" is the order of time).

However, it is also problematic to pay special attention to the use of volatile dual-check locks in previous versions of Java 5. The reason is that the previous Java 5 JMM (Java memory model) is flawed, the immediate declaration of variables into volatile can not completely avoid reordering, mainly volatile variables before and after the code still has a reordering problem. This volatile screen reordering problem is fixed in Java 5, so you can safely use volatile after that.

I believe you will not like this complex and hidden problem, of course, we have a better way to implement a single example of thread safety. A hungry man-static final field

This approach is very simple, because instances of a singleton are declared static and final variables, initialized when the class is first loaded into memory, so creating the instance itself is thread-safe.

The public class singleton{
    //class is loaded with the initialization of the
    private static final Singleton instance = new Singleton ();
    
    Private Singleton () {} public

    static Singleton getinstance () {return
        instance;
    }
}

If the writing is perfect, there is no need to nag so many double check locks. The disadvantage is that it is not a lazy load mode (lazy initialization), which is initialized at the start of the load class, even if the client does not invoke the GetInstance () method. A hungry man creation is not possible in some scenarios: for example, Singleton instance creation is a dependency parameter or a configuration file, and a method setting parameter must be invoked before getinstance (), so that this single example cannot be used. Static internal class static nested class

I prefer to use the static inner class method, which is also recommended in the "effective Java".

public class Singleton {  
    private static class Singletonholder {  
        private static final Singleton INSTANCE = new Sing Leton ();  
    }  
    Private Singleton () {} public  
    static final Singleton getinstance () {return  
        singletonholder.instance; 
    }  
}

This method of writing still uses the JVM's own mechanism to ensure thread-safety issues; Because Singletonholder is private, there is no way to access it except for getinstance (), so it is lazy; there is no synchronization when reading instances, no performance flaws, and no reliance on JDK version. Enum enum

It's too easy to write a single example with enumerations. This is also its biggest advantage. The following code is the usual way to declare an enumeration instance.

public enum easysingleton{
    INSTANCE;
}

We can access the instance through Easysingleton.instance, which is much simpler than calling the GetInstance () method. Creating enumerations By default is thread-safe, so you don't have to worry about double checked locking, and you can prevent deserialization from causing a new object to be recreated. But it's still rare to see someone write this, probably because you're not familiar with it. Summarize

In general, the single case pattern has five kinds of wording: lazy, a hungry man, double check lock, static internal class, enumeration. Above all is the realization of thread safety, the first method given at the beginning of the article is not correct.

As far as I am concerned, it would be nice to use the A hungry man directly in general, if the explicit requirement to lazy load (lazy initialization) will tend to use static internal classes, if it involves deserialization to create an object, try to use the enumeration method to implement the single example.

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.