七种单例模式(懒汉式和饿汉式)

单例模式

懒汉式和饿汉式的区别

懒汉式:需要使用时才加载
饿汉式:不管要不要使用时,在类装载的时候就创建一个静态堆对象

1 . 懒汉式(线程不安全)

但是在多线程时候,很多线程不能正常工作

public class singlePattern1 {

    private static singlePattern1 instance; //采用一个静态对象

    public static singlePattern1 getInstance(){

        if(instance == null){ // 如果没有该对象的实例
            instance = new singlePattern1(); // 实例化该对象
        }

        return instance;
    }
}

2 . 懒汉式(线程安全)

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading
但是,遗憾的是,效率很低,99%情况下不需要同步。

public class singlePattern2 {
    private static singlePattern1 instance; //采用一个静态对象

    public static synchronized singlePattern1 getInstance() {//持有该对象的锁的线程可以执行该对象方法,,使线程操作有序的操作该方法

        if (instance == null) { // 如果没有该对象的实例
            instance = new singlePattern1(); // 实例化该对象
        }

        return instance;
    }
}

3 . 饿汉式(基于classloder机制)

这种方式基于classloder机制避免了多线程的同步问题
不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种
在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其 他的方式(或者其他的 静态方法)导致类装载这时候初始化instance显然没有达到lazy loading的效果

public class singlePattern3 {

    private static singlePattern3 instance = new singlePattern3();//在类的装载就直接实例化

    public static singlePattern3 getInstance() {
        return instance;
    }
}

4 . 饿汉式(类加载就执行)

和第3种差不多,java的静态代码块,也是在加载的时候就执行,即在装载类的时候就实例化对象,初始化对象就实例化对象

public class singlePattern4 {
    private static singlePattern4 instance = null;

     static { // 静态代码块 ,,加载类的时候,就执行
        instance = new singlePattern4();
    }

    public static singlePattern4 getInstance() {
        return instance;
    }
}

5 .懒汉式(基于classloder的机制)

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,
它跟第三种和第四种方式不同的是(很细微的差别):
第三种和第四种方式是只要singlePattern5类被装载了,那么instance就会被实例化(没有达到lazy loading效果)
而这种方式是singlePattern5类被装载了,instance不一定被初始化
因为singletonHolder类没有被主动使用,只有显示通过调用getInstance方法时, 才会显示装载singletonHolder类,从而实例化instance。
想象一下,如果实例化instance很消耗资源,我想让他延迟加载
另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载
那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。


public class singlePattern5 {

    private static class singletonHolder {//静态方法私有化
        private static final singlePattern5 instance = new singlePattern5();
    }

    public static final singlePattern5 getInstance() {
        return singletonHolder.instance;
    }

}

6 . 枚举(安全性高,防止反射打破单例)

这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,使用枚举实现的单例模式,不但可以防止利用反射强行构造单例子对象,而且枚举类对象在被反序列化的时候,保证反序列的返回结果是同一对象,
对于其他方法实现单例模式,如果既想要做到可序列化,又想反序列化为同一对象则必须实现readResolve方法

public enum singlePattern6 {
    INSTANCE;
}

7 . 双重校验机制(非常安全)

使用双重锁检测机制,确保并发情况下instance 对象不会被重复的初始化,
使用volatile 修饰符,防止指令重排引发初始化问题
保证线程的安全

public class singlePattern7 {
    private singlePattern7() {

    }

    private volatile static singlePattern7 instance = null;

    public static singlePattern7 getInstance() {

        if (instance == null) { // 双重检验机制

            synchronized (singlePattern7.class) { //设置同步锁

                if (instance == null) { // 双重检验机制
                    instance = new singlePattern7();
                }
            }
        }
        return instance;
    }
}

总结:

对于以上七种单例,第一种是最不推荐的,在多线程并发的时候,单例直接就不能维持,其它六种
在一些条件下能够保证单例,为什么说在一些条件下能保证单例? 是因为单例即使很安全,但是反
射能打破单例。枚举的情况就不会被打破单例。

反射打破单例

      Constructor con = singlePattern.class.getDeclaredConstructor();//获取构造器
      con.setAccessible(true); //设置为该对象为可访问

        //构造两个对象
      singlePattern instance1= (singlePattern)con.newInstance();
      singlePattern instance2 = (singlePattern)con.newInstance();
可以通过类的反射获取类的实例对象,即单例被打破了,但是枚举类不可以反射。所以枚举,不能
被反射打破。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值