设计模式-单例模式的几种实现方式

  • PS: 若文章字体偏大或者偏小,建议通过 ctrl键+鼠标滑轮 进行修改,以提升阅读效果.(带来不便,请谅解!)

懒汉式:

1. 线程不安全:

//是否 Lazy 初始化:是

//是否多线程安全:否

//实现难度:易
public  class  Singleton{
    
    private  static  final Singleton  SINGLETON =new Singleton() ;
    
    public  Singleton getInSingleton(){
        return SINGLETON; 
    }
}
  1. 线程安全:

饿汉式:

双重锁校验(Double check lock) 实现

http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

// Works with acquire/release semantics for volatile
// Broken under current semantics for volatile
  class Foo {
        private volatile Helper helper = null;
        public Helper getHelper() {
            if (helper == null) {
                synchronized(this) {
                    if (helper == null)
                        helper = new Helper();
                }
            }
            return helper;
        }
    }

两个问题

单例模式为什么要加static关键字?

static :

public class SingletonLazy {
    //使用volatile 原因如下:
//    避免指令重排序
    private   volatile SingletonLazy instance = null;
    SingletonLazy() {
    // jvm执行顺序:
//        1. 分配内存空间
//        2. init, 数据初始化
//        3. 为对象指向分配的内存空间
    }
    public SingletonLazy getInstance() {
        if (instance == null) {
            synchronized(SingletonLazy.class){ // 锁住 singletonLazy , 
           // synchronized (this) {// 锁this, 即使添加了static 关键字后依旧会出错, 
                if (instance == null) {
                    instance = new SingletonLazy();
                }
            }
        }
        return instance;

    }
}

测试类:

public class Main {
    public static void main(String[] args) {
        for(int i= 0 ; i<100;i++){
            new  Thread(()->{
                printInstanceDetail();
            },"thread"+i).start();
        }
    }

    private static void printInstanceDetail() {
        String name = Thread.currentThread().getName();
        SingletonLazy inSingleton = new SingletonLazy().getInstance();
        System.out.println(name+": "+inSingleton);
    }
}

结果示图: (锁this 指针, 不加static 关键字 )

"-javaagent:C:\IntelliJ IDEA 2019.1.3\lib\idea_rt.jar
//省略.... thread6: design_patten.singleton.SingletonLazy@76ee89dd thread8: design_patten.singleton.SingletonLazy@168193e5 thread9: design_patten.singleton.SingletonLazy@37cb6108 thread10: design_patten.singleton.SingletonLazy@4025fef1 thread11: design_patten.singleton.SingletonLazy@589c11d1 thread12: design_patten.singleton.SingletonLazy@1468ed9c Process finished with exit code 0

第一次测试:添加static 关键字后 : (锁this,加static 关键字)

thread2: design_patten.singleton.SingletonLazy@278e4dc4
thread5: design_patten.singleton.SingletonLazy@278e4dc4
thread4: design_patten.singleton.SingletonLazy@278e4dc4
thread6: design_patten.singleton.SingletonLazy@278e4dc4
thread1: design_patten.singleton.SingletonLazy@6223c513

第二次测试: 锁 类对象SingletonLazy.class ,加 static 关键字

thread5: design_patten.singleton.SingletonLazy@76ee89dd
thread3: design_patten.singleton.SingletonLazy@76ee89dd
thread4: design_patten.singleton.SingletonLazy@76ee89dd
thread7: design_patten.singleton.SingletonLazy@76ee89dd
thread6: design_patten.singleton.SingletonLazy@76ee89dd
thread2: design_patten.singleton.SingletonLazy@76ee89dd
thread0: design_patten.singleton.SingletonLazy@76ee89dd
thread8: design_patten.singleton.SingletonLazy@76ee89dd
thread1: design_patten.singleton.SingletonLazy@76ee89dd
thread9: design_patten.singleton.SingletonLazy@76ee89dd

单例模式为什么要用final 关键字?

final修饰的变量值不会改变。

但是在多线程的环境中,它还会保证两点,

​ **1. 其他线程所看到的final字段必然是初始化完毕的。 **

2. final修饰的变量不会被程序重排序。

声明为final的变量,必须在类加载完成时已经赋值, 是什么意思呢?

就是,如果你是final非static成员,必须在构造器、代码块、或者直接定义赋值

如果是final static 成员变量,必须直接赋值 或者在 静态代码块中赋值

然而直接赋值 或 静态代码块中赋值 就变成饿汉模式了,

所以懒汉模式中,不能用final修饰

总结:

参考: