详解单例模式+代码实现

  • Post author:
  • Post category:其他


一 什么是单例设计模式?

1.1 定义

属于创建型模式,单例类需要自己创建它的对象,并且保证一个类中只有一个对象的实例。满足以下三点:

  1. 单例类只有一个实例

  2. 单例类必须为自己创建唯一的实例

  3. 单例类必须给其他对象提供者一个实例

1.2 优点

  • 因为只有一个实例,开销小

  • 在需要频繁创建或者销毁的情况下,可以避免对资源的多重占用

1.3 缺点

  • 没有抽象层,很难扩展

1.4 应用场景

  • 大家都要喝水,但是没必要每人家里都打一口井是吧,通常的做法是整个村里打一个井就够了,大家都从这个井里面打水喝


  • 日志管理、打印机、数据库连接池、应用配置

二 常见写法

2.1 饿汉式


定义:

顾名思义,它在类加载的时候就创建对象


优点:

线程安全,执行效率高


缺点:

在类加载的时候就初始化,浪费内存空间


线程安全:

通过

类加载机制

来保证线程安全。详细源码解析:

深度分析Java的ClassLoader机制


代码实现:

/**
 * @ClassName Singleton
 * @Description: //TODO 饿汉式单例
 * @Author wyq
 * @Date 2022/9/2 23:40
 */
public class Singleton {
    //私有化构造方法,这样类就不会被实例化
    private Singleton() {
    }
​
    //创建Singleton()的一个对象,在类加载时初始化
    private final static Singleton instance = new Singleton();
​
    //对外提供的方法,获取唯一的对象
    public static Singleton getInstance() {
        return instance;
    }
}

2.2 懒汉式


定义:

懒加载,使用的时候再创建对象。


优点:

第一次调用的时候才初始化,避免内存浪费


缺点:

必须要加

synchronized

锁才能保证线程安全,加锁后效率低


线程不安全,代码实现:

/**
 * @ClassName LazySingleton
 * @Description: //TODO 懒汉式,线程不安全
 * @Author wyq
 * @Date 2022/9/3 0:03
 */
public class LazySingleton {
    private LazySingleton() {
    }
​
    private static LazySingleton instance;
​
    public static LazySingleton getInstance() {
        //判断为null的情况下才创建对象
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }
}

可以通过加锁来保证线程安全,如果将

synchronized

锁添加在方法上,虽然保证了线程安全,但是每次调用

getInstance()

获取对象的时候都需要加锁和释放锁,使效率降低。


线程安全,代码实现:

/**
 * @ClassName LazySingletonSafe
 * @Description: //TODO 懒汉式,⽅法上⾯添加 synchronized 保证线程安全
 * @Author wyq
 * @Date 2022/9/3 0:11
 */
public class LazySingletonSafe {
    private LazySingletonSafe() {
    }
​
    private static LazySingletonSafe instance;
​
    //方法上添加synchronized,保证线程安全
    public synchronized static LazySingletonSafe getInstance() {
        if (instance == null) {
            instance = new LazySingletonSafe();
        }
        return instance;
    }
}

2.3 双重检验锁( double-checked locking)


属于懒汉式的一种优化


优点:

懒加载,线程安全,效率较⾼


解析:

双重指的是两次非空判断,锁是synchronization。


  1. 第一次判断:

    如果这个实例已经存在,就不需要创建,直接返回


  2. 加锁:

    保证一次只有一个线程操作


  3. 第二次判断:

    如果不幸有多个线程同时到锁的位置,进来的第一个线程才会进行初始化,而其他线程会被判空拦截直接返回


  4. volatile:

    用来修饰

    instance

    ,保证所有线程可以实时看到它的状态。


代码实现:

/**
 * @ClassName DCLSingleton
 * @Description: //TODO 双重检验锁
 * @Author wyq
 * @Date 2022/9/3 0:20
 */
public class DCLSingleton {
    private DCLSingleton() {
    }
​
    //volatile保证可见性并禁止指令重排序
    private volatile static DCLSingleton instance;
​
    public static DCLSingleton getInstance() {
        //第一重检验,如果这个实例已经存在,就不需要创建,直接返回
        if (instance == null) {
            //加锁保证一次只有一个线程操作
            synchronized (DCLSingleton.class) {
                //第二重检验,如果不幸有多个线程同时到锁的位置,进来的第一个线程才会进行初始化,
                //而其他线程会被判空拦截直接返回
                if (instance == null) {
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}



版权声明:本文为weixin_46047193原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。