什么是单例模式

1.什么是单例模式

在内存中只会创建一次且仅创建一次对象的设计模式。在程序中多次使用同一个 对象且作用相同时,为了防止频繁的创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一对象。

单例模式有以下特点:
  1、单例类只能有一个实例。
  2、单例类必须自己创建自己的唯一实例。
  3、单例类必须给所有其他对象提供这一实例。

2.单例模式的类型

·懒汉式:在真正需要使用对象时才去创建该单例类对象

·饿汉式:在类加载时已经创建好该单例对象,等待程序使用

·枚举

懒汉式创建单例对象

懒汉式创建对象的方法是在程序使用对象前,先判断该对象是否已经实例化(判空),若已经实例化直接返回该类对象,否则先执行实例化操作。

public class Singleton {
    private static Singleton singleton;
    private Singleton(){}
        public static Singleton getInstance(){
            if (singleton==null) {
            singleton=new Singleton();
            }
            return singleton;
        
    }
}

饿汉式创建单例对象

饿汉式在类加载时已经创建好该对象,在程序调用时直接返回该单例对象即可,即我们在编码时就已经指明了要马上创建这个对象,不需要等到被调用时再去创建。默认认为程序在启动时已经创建好了这个对象。

public class Singleton {
//    private static Singleton singleton;
//    private Singleton(){}
//        public static Singleton getInstance(){
//            if (singleton==null) {
//            singleton=new Singleton();
//            }
//            return singleton;
//
//    }
    private static final Singleton singleton=new Singleton();
    private  Singleton(){}
    public static Singleton getInstance(){
        return singleton;
    }
}

3.懒汉式在多线程下的表现

懒汉式前提判断对象是否为空,但是如果有两个线程同时判断对象没有实例化二者都会去实现一个Singleton对象,就变成双例了。这就是线程安全问题,一般我们都会在方法上加锁,或者对类对象加锁。

public class Singleton {
    private static Singleton singleton;
    private Singleton(){}
        public static synchronized Singleton getInstance(){
            if (singleton==null) {
            singleton=new Singleton();
            }
            return singleton;
    }


    public static  Singleton getInstance(){
        synchronized (Singleton.class) {
            if (singleton == null) {
                singleton = new Singleton();
            }
        }
        return singleton;
    }

这样是规避了线程安全问题

但引来另一个问题:每次去获取对象都要先获取锁,并发性能非常差。

Double Check 双重校验+Lock 加锁

public class Singleton {
    private static Singleton singleton;

    private Singleton() {
    }

    public static Singleton getInstance() {
        if (singleton == null) {//如果不为空直接返回对象,为空则判断下面
            synchronized (Singleton.class) {多个线程进行争抢 只允许一个线程 首先再次判断是否为空 然后论到其他线程时判断不为空直接输出对象
                if (singleton==null) {
                singleton = new Singleton();
            }  }
        }
        return singleton;
    }
}

4.指令重排

创建对象,在JVM中会经历三步:

(1)为singleon分配内存空间

(2)初始化singleton对象

(3)将singleton指向分配好的内存空间

指令重排是指:JVM在保证最终结果正确的情况下,可以不按照程序编码的顺序执行,尽可能提高程序的性能

在这三步中,第二三步有可能会发生指令重排现象,创建对象顺序变为1-3-2,会导致多线程获取对象时,有可能线程A创建对象的过程中,执行了1、3步骤,线程B判断singleton已经不为空,获取到未初始化的singleton对象,就会报NPE异常。

使用volatile关键字可以防止指令重排序

5.枚举实现单例模式

优势1:代码对比饿汉式与懒汉式来说,更加地简洁

其次,既然是实现单例模式,那这种写法必定满足单例模式的要求,而且使用枚举实现时,没有做任何额外的处理。

优势2:它不需要做任何额外的操作去保证对象单一性与线程安全性

我写了一段测试代码放在下面,这一段代码可以证明程序启动时仅会创建一个 Singleton 对象,且是线程安全的。

我们可以简单地理解枚举实现单例的过程:在程序启动时,会调用Singleton的空参构造器,实例化好一个Singleton对象赋给INSTANCE,之后再也不会实例化
优势3:使用枚举可以防止调用者使用反射序列化与反序列化机制强制生成多个单例对象,破坏单例模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值