单例模式的4种写法

2018-02-28  本文已影响0人  蜀山白豆腐
一直知道单例模式有4种,但是都没有写过。在这复习之际把这些东西写一边巩固下基础。

1.饿汉式

public class Singleton1 {
    private Singleton1() {};//私有的无参构造器
    
    private static Singleton1 instance = new Singleton1();
    
    private static Singleton1 getInstance(){
        return instance;
    }
}

使用起来简单方便,但是在单例较多的情况下内存占用会比较高。

2.懒汉式

public class Singleton2 {
    private Singleton2(){};
    
    private volatile static Singleton2 instance;//加上volite防止指令重排
    
    private static Singleton2 getInstance(){
        if (instance == null) {
            synchronized(Singleton2.class){//加锁防止多线程生成多个实例
                if (instance == null) {
                    instance = new Singleton2();//指令重排序,先完成赋值,但构造函数还没执行完
                }
            }
        }
        return instance;
    }
}

采用了双重检查,线程安全

用上面的例子,简单复习下volitile的用法 之后有时间可能会写个详细的
instance = new Singleton2()可以分解为三部分
1 memory=allocate();// 分配内存
2 ctorInstanc(memory) //初始化对象
3 instance=memory //设置instance指向刚分配的地址

JVM出于优化需要可能进行指令重排就会出现1->3->2的情况,多线程的情况下instance还没有初始化之前其他线程就会在外部检查到instance不为null,而返回还没有初始化的instance,就会有问题

JVM不会将volatile变量上的操作与其他内存操作一起重新排序,保证检测instance的状态时都是最新的

3.静态内部类

public class Singleton3 {
   private Singleton3(){};
   
   private static Singleton3 getInstance(){
       return Holder.instance;
   }
   
   private static class Holder{
       private static Singleton3 instance = new Singleton3();
   }
   
}

JVM保证在类加载的过程中static代码块在多线程或者单线程下都正确执行,且仅执行一次。内部类不会在类的外部被使用,只有在调用getInstance()方法时才会被加载,解决了延迟加载以及线程安全的问题。

4.使用枚举

public enum Singleton4 {
    INSTANCE;
    
    private Singleton4(){};
    
}

应该是最简单的方法吧,枚举构造函数为私有,不能再创建枚举对象,枚举对象的声明和初始化都是在static块中,由JVM的ClassLoader机制保证了线程的安全性。
Singleton4.INSTANCE就是类Singleton4的唯一实例。

上一篇下一篇

猜你喜欢

热点阅读