Singleton Pattern in Java and Patterns

The singleton pattern
in "JAVA and Patterns" In Dr. Yan Hong's "JAVA and Patterns" book, the singleton pattern is described at the beginning:
as an object creation pattern, the singleton pattern ensures that only a class has only one instance, and automatically Instantiate and provide this instance to the whole system. This class is called a singleton class


The structure
of the singleton pattern The characteristics of the singleton pattern:

  • Singleton pattern can only have one instance
  • A singleton class must create its own unique instance by itself
  • A singleton class must provide this instance to all other objects

    Hungry singleton

public class EagerSingleton{
    private static EagerSingleton instance = new EagerSingleton();
    //私有构造方法
    private EagerSingleton(){}

    //静态工厂方法
    public static EagerSingleton getInstance(){
        return instance;
    }

}

In the above example, when the class is loaded, the static variable instance will be initialized. At this time, the private constructor of the class will be called. At this time, the only instance of the singleton class will be created
. Typical space for space, an instance of the class will be created when the class is loaded, whether you use it or not, create it first, and then every time you call it, you don't need to judge, saving running time.

Lazy Man Pattern Singleton Pattern

public class LazySingleton{
    privae static LazySingleton instance = null;
    //私有默认构造子
    private LazySingleton(){}
    //静态工厂方法
    public static synchronized LazySingleton getInstance(){
        if(instance == null){
            instance  = LazySingleton();
        }
        return instance;
    }
}

The above lazy-man-style singleton pattern class implementation uses synchronization for static factory methods to deal with multi-threaded environments.
Lazy-man style is actually a more vivid title. Since it is lazy, there is no rush when creating object instances. It will wait until the object instance is about to be used before it will be created. Lazy people will always perform work when they can't escape, so the object instance will not be created when the object is loaded.
The lazy style is a typical time-for-space, that is, every time an instance is obtained, a judgment is made to see if an instance needs to be created, which wastes the judgment time. Of course, if no one uses it all the time, the instance will not be created, which saves memory space.
Because Lazy implementations are thread-safe, which slows down the entire access and has to judge each time.

So is there a better way to do it?

Double-checked shackles
can be implemented in the way of "double-checked shackles", which can achieve thread safety without greatly affecting performance. So what is "double-checked shackles" The
so-called "double-checked shackles" mechanism refers to The thing is: it is not necessary to synchronize every time you enter the getInstance method, but it is not synchronized first. After entering the method, first check whether the instance exists. If it does not exist, perform the following synchronization block. This is the first check. After entering the synchronization block, Check again if the instance exists, if not, create an instance in sync, this is the second check. In this way, only one synchronization is required, thereby reducing the time wasted in multiple judgments under synchronization.
The implementation of the "double-checked shackles" mechanism uses the keyword volatile, which means: the value of the variable modified by volatile will not be cached by the local thread, and all reads and writes to the changed variable are directly operated on the shared memory, thus ensuring that Multiple threads handle variables correctly..

Note: In java1.4 and previous versions, many JVM implementation problems for the volatile keyword will lead to the failure of "double-checked locking", so the "double-checked locking" mechanism can only be used in java5 and above. Version.

public class Singleton{
    private volatile static Singleton instance = null;
    private Singleton(){}
    public static Singleton getInstance(){
        //先检查实例是否存在,如果不存在才进入下面的同步块
        if(instance == null){
            //同步块,线程安全的创建实例
            Synchronized(Singleton.class){
                //再次检查实例是否存在,如果不存在建立真正的实例
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;

    }

}

This implementation allows for thread-safe instance creation without too much performance impact. It only synchronizes when the instance is created for the first time, and it does not need to be synchronized later, thus speeding up the operation.

Tip: Since the volatile keyword may mask some necessary code optimizations in the virtual machine, the running efficiency is not very high. Therefore, it is generally recommended that there is no special need, do not use. That is to say, although the "double-checked locking" mechanism can be used to implement thread-safe singletons, it is not recommended to use them in large numbers and can be selected according to the situation.

According to the above analysis, the two common singleton implementation methods have small defects, so is there a solution that can achieve both delayed loading and thread safety?

Lazy initialization holder class模式

1. Corresponding basic knowledge
What is a class-level inner class?
Simply put, a class-level inner class refers to a member-type inner class with static modification. If a member-type inner class without static modification is called an object-level inner class The
class-level inner class is equivalent to the static component of its outer class, and there is no dependency between the object and the outer class object, so it can be created directly. An instance of an object-level inner class is bound to an instance of the outer object.
In class-level inner classes, static methods can be defined. Only static member methods or member variables in the outer class can be referenced in a static method.

A class-level inner class is equivalent to a member of its outer class and is loaded only when it is used for the first time.

Knowledge of multi-threaded default synchronization locks
In multi-threaded development, in order to solve concurrency problems, synchronization is mainly controlled by using synchronized to mutex locks. However, in some cases, the JVM has implicitly performed synchronization for you, these In this case, you don't have to do synchronization control yourself. These situations include:

  • When data is initialized by static initialization (fire static{initializer in block} on static field)
  • When accessing final fields
  • When creating an object before creating a thread
  • When the thread can see the object it is about to process

    2. The idea of ​​the solution
    If you want to achieve thread safety very simply, you can use static initializers, which can ensure thread safety by the JVM. For example, the previous Hungry-style implementation. But in this way, it will not be possible. Wasting some space? Because of this implementation, the object is initialized when the class is loaded, whether you need it or not.
    If there is a way to not initialize the object when the class is loaded, a feasible way is to use a class-level inner class to create object instances in this class-level inner class. In this way, as long as this class-level inner class is not used, no object instance will be created, thus achieving both lazy loading and thread safety.

public class Singleton{

    private Singleton(){}
    /**
    * 类级的内部类,也就是静态的成员式内部类,该内部的实例与外部类的实例
    * 没有绑定关系,而且只有被调用到时才会装载,从而实现延时加载
    *
    */
    private static class SingletonHolder{
        //静态初始化,由JVM保证线程安全
        private static Singleton instance = new Singleton();
    }

    public static Singleton getInstance(){

        return SingletonHolder.instance;
    }

}

When the getInstance method is called for the first time, it reads SingletonHolder.instance for the first time, causing the SingletonHolder class to be initialized; and when the class is loaded and initialized, its static field will be initialized, thereby creating an instance of Singleton , Because it is a static language, it will only be initialized once when the virtual machine loads the class, and the virtual machine will ensure its thread safety.
The advantage of this mode is that the getInstance method is not synchronized, and only executes a domain access, so lazy initialization doesn't add any access cost.

Singletons and Enums
Single-element enumerations have become the best way to implement Singletons. Implementing a singleton with an enumeration is as simple as writing an enumeration type that contains a single element.

public enum Singleton{
    //定义一个枚举的元素,它就代表了Singleton的一个实例
    uniqueInstance;
    //单例可以有自己的操作
    public void singleonOperation(){
        //功能处理
    }
}

Using enumeration to implement single instance control is more concise, and provides a serialization mechanism for free, and is fundamentally guaranteed by the JVM to absolutely prevent multiple instantiations. It is a more concise, efficient, and safe way to implement singletons. .

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325759976&siteId=291194637