单例模式(Singleton Pattern)

一、单例模式的定义与特点

1.1 定义

确保一个类只有一个实例,并提供一个全局访问点。

1.2 单例类结构图
单例类结构图
1.3 条件
  • 将构造方法私有化,使其不能在类的外部通过new关键字实例化该类对象。
  • 在该类内部产生一个唯一的实例化对象,并且将其封装为private static类型。
  • 定义一个静态方法返回这个唯一对象。

二、单例模式-饿汉式

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

public class Singleton {

    // 将自身实例化对象设置为一个属性,并用static、final修饰
    private static final Singleton instance = new Singleton();
    
    // 构造方法私有化
    private Singleton() {}
    
    // 静态方法返回该实例
    public static Singleton getInstance() {
        return instance;
    }
}
  • 优点:实现起来简单,没有多线程同步问题。

  • 缺点:当单例类Singleton被加载的时候,会初始化static的instance,静态变量被创建并分配内存空间,从这以后,这个static的instance对象便一直占着这段内存(即便你还没有用到这个实例)。

三、单例模式-懒汉模式

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

3.1 非线程安全方式
public class Singleton {

    // 将自身实例化对象设置为一个属性,并用static修饰
    private static Singleton instance;
    
    // 构造方法私有化
    private Singleton() {}
    
    // 静态方法返回该实例
    public static Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 优点:实现起来比较简单,当类SingletonTest被加载的时候,静态变量static的instance未被创建并分配内存空间,当getInstance方法第一次被调用时,初始化instance变量,并分配内存,因此在某些特定条件下会节约了内存。

  • 缺点:在多线程环境中,这种实现方法是完全错误的,根本不能保证单例的状态。

3.2 线程安全方式-同步方法
public class Singleton {

    // 将自身实例化对象设置为一个属性,并用static修饰
    private static volatile Singleton instance;
    
    // 构造方法私有化
    private Singleton() {}
    
    // 静态方法返回该实例,加synchronized关键字实现同步
    public static synchronized Singleton getInstance() {
        if(instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}
  • 优点:在多线程情形下,保证了“懒汉模式”的线程安全。

  • 缺点:众所周知在多线程情形下,synchronized方法通常效率低,显然这不是最佳的实现方案。

3.3 线程安全方式-DCL双检查锁机制(DCL:double checked locking)
public class Singleton {

    // 将自身实例化对象设置为一个属性,并用static修饰
    private static volatile Singleton instance;
    
    // 构造方法私有化
    private Singleton() {}
    
    // 静态方法返回该实例
    public static Singleton getInstance() {
        // 第一次检查instance是否被实例化出来,如果没有进入if块
        if(instance == null) {
            synchronized (Singleton.class) {
                // 某个线程取得了类锁,实例化对象前第二次检查instance是否已经被实例化出来,如果没有,才最终实例出对象
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

DCL双检查锁机制是单例模式的最佳实现方式。内存占用率高,效率高,线程安全,多线程操作原子性。

四、单例模式应用场景

线程池、缓存、对话框、日志对象、Spring Bean工厂

五、知识点补充

在3.2 和 3.3 的代码中有一个Java关键字volatile,该关键字确保当instance变量被初始化成Singleton实例时,过个线程正确地处理instance变量。

volatile的概念:volatile与synchronized关键字是多线程并发编程中非常重要的知识点,通常被用于修饰变量。相比于synchroinized来说,volatile要轻量很多,执行的成本会更低。原因是volatile不会引起线程上下文的切换和调度,但是它与synchronized的意义其实是有区别的。synchronized关键字主要体现的是互斥性,而volatile体现的便是可见性、原子性。从根本上来说,volatile用于多线程之间内存的共享。

参考
设计模式之单例模式
java线程同步之volatile

最后给大家送波福利

阿里云折扣快速入口

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容