单例模式(饿汉式、懒汉式、DCL单例、静态内部类单例、枚举单例)

一、用途

一个类只允许创建一个对象, 主要用于无状态工具类、全局配置信息等。

二、单例四大原则

  1. 构造私有
  2. 以静态方法或者枚举返回实例
  3. 确保实例只有一个, 尤其是多线程环境
  4. 确保反序列化时不会重新构造对象

三、几种实现方式

主要就是饿汉式(非懒加载)、懒汉式(懒加载)、是否线程安全、静态内部类、枚举。

1、饿汉式

简单, 静态常量实现, 类装载的时候进行了实例化, 避免了线程问题, 线程安全, 可以使用, 但是没有实现懒加载, 也就是所谓的饿汉式。

public class Singleton{
    private static final Singleton instance = new Singleton();
    private Singleton(){}
    public static Singleton getInstance(){
        return instance;
    }
}

2、DCL单例

双重检查, 实现了懒加载, 效率较高, 线程安全。

//DCL单例模式
public class Singleton{
    // 此处必须是volatile
    private static volatile Singleton singleton;
    private Singleton(){}
    // 注意: 方法必须是 public static
    public static Singleton getSingleton(){
       if(singleton == null){
           synchronized(Singleton.calss){
               if(singleton == null){
                   // 这里实际是三步 不是原子的
                   // 为Singleton分配一块内存M
                   // 在内存M上对Singleton进行初始化
                   // 将内存M地址赋值给singleton变量
                   singleton = new Singleton();
               }
           }
       } 
       return singleton;
    }
}

3、静态内部类单例

利用jvm加载的特性, 实现了懒加载, 避免了线程问题。

public class Singleton{
    private Singleton(){}
    // 静态内部类在外部类装载的时候并不会被加载
    // 类的静态属性只有在第一次装载时才会被实例化
    private static class Holder{
        private static final Singleton instance = new Singleton();
    } 
    public Singleton getInstance(){
        // 调用时才会装载内部类SingletonUtil 从而完成Singleton的实例化
        return Holder.instance;    
    }
}

4、枚举单例

public enum Singleton{
    INSTANCE;
    public static Singleton getInstance(){
        return INSTANCE;
    }
}

1) effective java 书中推荐使用的单例模式;

2) 不会受到反序列化、反射的破坏;

Java的序列化专门对枚举的序列化做了规定: 在序列化时, 只是将枚举对象的name属性输出到结果中, 在反序列化时通过java.lang.Enum的valueOf方法根据名字查找对, 而不是新建一个新的对象,所以防止了反序列化对单例的破坏;
对于反射, 枚举类同样有防御措: 反射在通过newInstance创建对象时会检查这个类是否是枚举类, 如果是枚举类就会throw new IllegalArgumentException(“Cannot reflectively create enum objects”);

3) 枚举编译后是一个继承自java.lang.Enum的final类, 单例是类中的static final实例;

// 枚举: 
public enum EnumTest{
	A,B;
}
// javap 更多: https://www.jianshu.com/p/6a8997560b05
// javap反编译后: 
javap EnumTest.class
Compiled from "EnumTest.java"
public final class EnumTest extends java.lang.Enum<EnumTest> {
  // 静态常量 jvm级别保证了单例
  public static final EnumTest A;
  public static final EnumTest B;
  public static EnumTest[] values();
  public static EnumTest valueOf(java.lang.String);
  static {};
}

四、单例模式存在的问题

  • 单例对 OOP 特性的支持不友好
  • 单例会隐藏类之间的依赖关系
  • 单例对代码的扩展性不友好
  • 单例不支持有参数的构造函数

五、重新理解单例

  1. 进程唯一的单例 上面说的都是单进程下的单例 不支持部署多个进程
  2. 线程唯一的单例 ThreadLocal 自己实现的话HashMap
  3. 集群唯一的单例 分布式锁 对象存放在哪需要考虑
  4. 多例: 指定N个实例… 推荐枚举, 还可以用map

六、参考

https://blog.csdn.net/weixin_36586120/article/details/105522491

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值