There are eight ways to write the singleton pattern. Isn’t it true that design patterns represent best practices?

Each singleton writing method can basically destroy the properties of its singleton, which brings security risks, so each singleton writing method is strengthened on the basis of the previous, but it will increase the space complexity or time complexity, there is no optimal usage, only the most appropriate usage.

Some of the eight methods are repeated, just written in a slightly different way. Each method is tested concurrently with multiple threads, and the number of times the constructor is executed is measured to see how many objects are created

[1] Method 1: Single case of hungry man

As the name implies, hungry people are hungrier and more impulsive. They enter the theme and create objects at the beginning.

  • The singleton object is created when the class is loaded
  • Only one instance is created, which is absolutely thread-safe. It is instantiated before the thread even exists. There can be no access security issues, and the JVM is thread-safe
  • Advantages: No lock, high execution efficiency, thread safety
  • Cons: Instantiation is done when the class is loaded, whether you use it or not, wasting memory (space for time)
public class Hungry {
    // Private constructor
    private Hungry(a){
        System.out.println(Thread.currentThread().getName());
    }
    // a private static reference to your own instance
    private static final Hungry hungry = new Hungry();
    // a static public method that returns its own instance
    public static Hungry getInstance(a){
        returnhungry; }}// Multithreaded concurrent testing
@Test
public void TestMain1(a){
    for (int i = 0; i <10; i++){newThread(() -> { Hungry.getInstance(); }).start(); }}Copy the code

[2] Method two: lazy singleton

Lazy mode, which is lazy, procrastinating, instantiating the object when calling the instance method, I don’t give you a new object in the beginning, you come to me, I’ll create another object for you

  • Instantiate the object only when the instance method is called
  • ingetInstanceMethod is not thread safe and creates multiple instances
  • Advantages: No one has been using it, it will not create instances, saving memory space
  • Disadvantages: wasted judgment time, reduced efficiency (time for space), thread insecurity
public class LazyMan {
    // Private constructor
    private LazyMan(a){
        System.out.println(Thread.currentThread().getName());
    }
    // a private static reference to your own instance
    private static LazyMan lazyMan = null;
    // a static public method that returns its own instance
    public static LazyMan getInstance(a){
        if(lazyMan == null){
            lazyMan = new LazyMan();
        }
        returnlazyMan; }}// Multithreaded concurrent testing
@Test
public void TestMain2(a){
    for (int i = 0; i <10; i++){newThread(() -> { LazyMan.getInstance(); }).start(); }}Copy the code

[3] Method 3: Slacker singleton reinforcement (synchronous method)

Method 2 has the hidden danger of unsafe thread, we can strengthen it by adding lock, method declaration add synchronized, synchronized method

  • As with method two, the object is instantiated only when the instance method is called
  • Lock method declarations to keep them thread safe
  • Advantages: No instance is created if no one is using it, saving memory space, and thread-safe
  • Disadvantages: Lock determination is required every time an instance is created, wasting judgment time and reducing efficiency (time for space)
public class LazyManSyn {
    // Private constructor
    private LazyManSyn(a){}
    Private static references create objects
    private static LazyManSyn lazyManSyn = null;
    // The synchronous method creates the instance object
    public static synchronized LazyManSyn getInstance(a){
        if (lazyManSyn == null){
            lazyManSyn = new LazyManSyn();
        }
        returnlazyManSyn; }}// Multithreaded concurrent testing
@Test
public void TestMain3(a){
    for (int i = 0; i < 10; i++) {
        newThread(()->{ System.out.println(LazyManSyn.getInstance().hashCode()); }).start(); }}Copy the code

[4] Method 4: Slacker Singleton Reinforcement (DCL)

Method three is thread-safe, but every time you create an instance, you need to judge the lock, which is inefficient. We can strengthen it by double locking, so that there is no lock judgment when there is already an instance object. This method is called DCL lazy

  • As with method two, the object is instantiated only when the instance method is called
  • Prevent multiple instances with double locking (double judgment and locking)
    • The first if judgment: to improve execution efficiency, if the object is not empty, simply do not execute the following program
    • The second if judgment: Between the first if and synchronized lock, multiple threads can enter. If there is no second if, one thread can acquire the lock new object and release the lock, and the second thread can acquire the lock new object. The second if can avoid creating multiple objects.
    • Synchronized lock: By synchronizing code blocks
  • Advantages: Reduced lock judgment, compared with method 3 to improve efficiency, use double lock, consider the thread safe (more later)
  • Disadvantages: In fact, this method still exists thread unsafe hidden danger, inlazyManDCL = new LazyManDCL();When creating an object, it is not an atomic operation, and there is the possibility of reordering instructions, so it is possible to create multiple instances
public class LazyManDCL {
    // Private constructor
    private LazyManDCL(a){}
    // a private static reference to your own instance
    private static LazyManDCL lazyManDCL = null;
    // Double check lock mode
    public static LazyManDCL getInstance(a){
        if(lazyManDCL == null) {synchronized (LazyManDCL.class){
                if(lazyManDCL == null){
                    lazyManDCL = newLazyManDCL(); }}}returnlazyManDCL; }}// Multithreaded concurrent testing
@Test
public void TestMain3(a){
    for (int i = 0; i <10; i++){newThread(() -> { System.out.println(LazyManDCL.getInstance().hashCode()); }).start(); }}Copy the code

[5] Method 5: DCL enhancement

In DCL singleton mode, although certain thread-safety can be guaranteed through double locking, when new object, non-atomic operation causes instruction rearrangement, execute new LazyManDCL(); Is as follows:

  1. Allocating memory space
  2. Execute constructor
  3. Let’s point this object to this space

When executing code, the compiler and processor often reorder the instructions in the order 1 – >2 – >3 or 1 – >3 – >2 to improve performance. When executing code in the order 1 – >3 – >2, the following problems occur:

  • Thread A does 1 — >3, allocates memory space, and points this object to that space
  • Now thread B comes in, and since thread A is pointing to this space, the first if is judged to be false, so it returns the object directly, because there is no initialization of the object, an error will be reported

Therefore, we want to prevent instruction reordering by using the volatile keyword as follows, adding a volatile keyword to method four

  • Advantages: Thread safety
  • Disadvantages: Multiple judgments, waste of time, reduce efficiency
public class LazyManDCL {
    // Private constructor
    private LazyManDCL(a){}
    // A private static reference to its own instance, using volatile to prevent instruction reordering
    private static volatile LazyManDCL lazyManDCL = null;
    // Double check lock mode
    public static LazyManDCL getInstance(a){
        if(lazyManDCL == null) {synchronized (LazyManDCL.class){
                if(lazyManDCL == null){
                    lazyManDCL = newLazyManDCL(); }}}returnlazyManDCL; }}// Multithreaded concurrent testing
@Test
public void TestMain4(a){
    for (int i = 0; i <10; i++){newThread(() -> { System.out.println(LazyManDCL.getInstance().hashCode()); }).start(); }}Copy the code

[6] Method 6: Lazy singleton reinforcement (Static inner class)

Instances are created using static inner classes. The core principle of the static inner class singleton pattern is that the ASSIGNMENT of a static variable to a class is performed globally only once when the JVM loads it with only one classloader! .

  • Advantage 1: When the external class is loaded, the internal class is not loaded immediately, so it does not occupy memory
  • Advantage 2: Unlike DCL, multiple judgments are not required, which improves efficiency
  • Advantage 3: First callgetInstance()Method is used to load the VMSingleOneClass,It not only ensures thread-safety, but also ensures the uniqueness of instances, but also delays singleton instantiation
public class SingleOne {
    // Private constructor
    private SingleOne(a){}
    // Create an instance from a static inner class
    private static class InnerClass{
        private static final SingleOne SINGLE_ONE = new SingleOne();
    }
    // Return an instance of an object through an inner class
    public static SingleOne getInstance(a){
        returnInnerClass.SINGLE_ONE; }}// Multithreaded concurrent testing
@Test
public void TestMain5(a){
    for (int i = 0; i < 10; i++) {
        newThread(()->{ System.out.println(SingleOne.getInstance().hashCode()); }).start(); }}Copy the code

[6] Method 7: Enumerate singletons (best written)

All of the above methods ignore reflection. As we all know, reflection can destroy classes and can ignore private constructors. Therefore, all of the above singletons can be destroyed using reflection.

  • Enumeration is itself a singleton, a singleton in any case
  • Directly throughEnumSingle.INVALIDThe calling
  • Let the JVM help us with thread safety and single instance issues
public enum EnumSingle {
    INVALID;
    public void doSomething(a){}}// Multithreaded concurrent testing
@Test
public void TestMain6(a){
    for (int i = 0; i < 10; i++) {
        newThread(()->{ System.out.println(EnumSingle.INVALID.hashCode()); }).start(); }}Copy the code

conclusion

There are many ways to write singleton pattern, a little change may be another, but the most perfect or method seven enumeration singleton, but the most used or the first type, because it is simple, easy to understand, more suitable for developers. In fact, we do not need to stick to the perfect, the most appropriate is the best, with what way to solve the actual problem is more appropriate to use what way, do not pursue those unnecessary perfect. Like two people together, he (she) may be perfect enough, you like it very much, but because of various reasons is not so suitable, like is the joy at first sight, for a long time still sting, appropriate is you come and go, understand and chat, you are with the heart method or with their own can easily come to the method? Of course, it is best to both like and fit, no matter how hard it is, you will always encounter that you like and fit, after all, the earth is round, it is just a matter of time!