1.概述

单例模式的定义:一个类只能创建一个实例

为什么使用单例模式?

  • 频繁创建对象、管理对象是一件很耗费资源的事,如果只需要一个实例来使用可以节省资源
  • 表示全局唯一类:在业务概念上,如果有些数据在系统中只保存一份,那么就适合设计成单例模式
    • 例如:配置文件、唯一递增ID号码生成器

2.创建

创建单例模式其实很简单,分为三步:

  • 将构造函数私有化
  • 在类的内部创建实例
  • 提供获取唯一实例的方法

1.饿汉式单例

饿汉式的实现方式,在类加载的期间,就已经将 instance 静态实例初始化好了,所以,instance 实例的创建是线程安全的。

缺点:

  • 这样的实现方式不支持延迟加载实例。
  • 一开始就创建实例,一旦这个类没有被使用,那么就造成了资源浪费!
  1. /**
  2. * 饿汉式单例
  3. */
  4. public class EagerSingleton {
  5. //1-将构造函数私有化
  6. private EagerSingleton(){};
  7. //2-在类的内部创建实例
  8. private static EagerSingleton eagerSingleton = new EagerSingleton();
  9. //3-提供获取唯一实例的方法
  10. public static EagerSingleton getInstance(){
  11. return eagerSingleton;
  12. }
  13. }

2.懒汉式单例

由于懒汉式单例存在问题:无法延迟加载、可能造成内存浪费

所以引出了饿汉式单例=>只有在使用时才会创建【第一次使用时】

缺点

  1. 饿汉式单例缺点非常明显,在多线程情况下如果不加锁==>可能会创建多个实例
  2. **加锁!**===>影响并发效率!!【每次获取实例都会被锁(不管是否已创建)】
/**
 * 懒汉式单例
 */
public class LazySingleton {

    private LazySingleton(){};

    //初始化为null
    private static LazySingleton instance = null;

    //当使用该类时才会去创建
    //有线程安全问题==>需要加锁
    public static synchronized LazySingleton getInstance() {
        if(instance==null){
            instance = new LazySingleton();
        }

        return instance;
    }
}

3.双重检测DLC

由于懒汉式单例存在性能问题,所以引入双重检测的单例模式!

  • 首先,将锁的范围缩小==>只有通过第一层检测后才会竞争类锁
  • 其次,获取到锁后再做一次检测【同时只有一个线程获取】,之后创建实例【保证了只有一个实例】
/**
 * 双重检测单例
 */
public class DoubleChecked {

    private DoubleChecked(){};

    private static volatile DoubleChecked instance = null;

    public static DoubleChecked getInstance(){

        if(instance==null){ //第一层检测
            synchronized (DoubleChecked.class){ //类锁
                if(instance==null){//第二层检测
                    instance = new DoubleChecked();
                }
            }
        }

        return instance;
    }
}

当然,这个方法还可能会有指令重排的问题 ==> 由于instance = new DoubleChecked();不是原子操作。当他在实例化时【还未初始化】 此时其他线程来get,它判断不为null,但是获取到的是没有初始化的实例!

要解决也十分简单,加上我们的volatile关键字就可以了,volatile有内存屏障的功能

4.静态内部类

一种比双重检测更加简单的实现方法,那就是利用 Java 的静态内部类。

它有点类似饿汉式,但又能做到了延迟加载。

public class InnerSingleton {

    private InnerSingleton(){}

    //静态内部类不会在外部类加载时被加载
    private static class SingletonHolder{
        private static final InnerSingleton instance = new InnerSingleton();
    }

    public static InnerSingleton getInstance(){
        return SingletonHolder.instance;
    }
}

SingletonHolder 是一个静态内部类,当外部类 InnerSingleton 被加载的时候,并不会创建

SingletonHolder 实例对象。只有当调用 getInstance() 方法时,SingletonHolder 才会被加载,

这个时候才会创建 instance。insance 的唯一性、创建过程的线程安全性,都由JVM 来保证。

所以,这种实现方法既保证了线程安全,又能做到延迟加载。

5.枚举实现

public enum EnumSingleton{
    INSTANCE;
}

这种实现方式通过Java 枚举类型本身的特性,保证了实例创建的线程安全性和实例的唯一性。
**