单例模式

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。

单例模式有 3 个特点

  1. 单例类只有一个实例对象
  2. 该单例对象必须由单例类自行创建
  3. 单例类对外提供一个访问该单例的全局访问点

单例模式具体实现分为: 懒汉式和饿汉式

懒汉式

该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例

public class LazySingleton
{
    // 保证 instance 在所有线程中同步
    private static volatile LazySingleton instance=null;    
    // private 避免类在外部被实例化
    private LazySingleton(){}    
    public static synchronized LazySingleton getInstance()
    {
        // getInstance 方法前加同步
        if(instance==null)
        {
            instance=new LazySingleton();
        }
        return instance;
    }
}

另外一种是静态内部类形式
当我们在使用懒汉式单例模式时,需要在getInstance()方法中对instance进行null判断,而这种判断会在多线程环境下引发线程安全问题。为了保证线程安全,我们可以使用静态内部类的方式来实现单例模式。下面就请看我的Java代码示例演示:

public class Singleton {

    private Singleton() {
        // 私有构造函数,防止其它类实例化本类
    }
    
    // 静态内部类
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    // 获取本类实例的静态方法
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

在以上这个示例中,我们使用了静态内部类SingletonHolder。内部类的私有静态字段INSTANCE用来保存该类的单例实例,在getInstance()方法中调用静态内部类的INSTANCE字段来获取单例实例。这种方式可以保证线程安全,在多线程情况下也可以保证正确性。

使用这种方式,当我们第一次调用getInstance()方法时,JVM会加载Singleton类,这时内部类并不会被加载,因此SingletonHolder.INSTANCE并不会被创建。直到第一次调用getInstance()方法时,内部类SingletonHolder才会被加载,从而创建了SingletonHolder.INSTANCE单例实例。因此,这种方式可以又简单又安全地实现单例模式。

注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程非安全的问题。如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点

饿汉式

该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了

public class HungrySingleton
{
    // 类加载时就初始化
    private static final HungrySingleton instance=new HungrySingleton();
    private HungrySingleton(){}
    public static HungrySingleton getInstance()
    {
        return instance;
    }
}

饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的,可以直接用于多线程而不会出现问题



除此之外还有一种实现方式,采用枚举
```java
public class SingletonEnum {

    private SingletonEnum() {}

    public static SingletonEnum getInstance() {
        return EnumHolder.INSTANCE.getInstance();
    }

    private enum EnumHolder {
        INSTANCE;
        private SingletonEnum instance = null;

        private EnumHolder() {
            instance = new SingletonEnum();
        }

        private SingletonEnum getInstance() {
            return instance;
        }
    }
}
```
借助enum枚举类的特性,直接解决了多线程同步问题