🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### 单例设计模式之懒汉实现+双重检查锁定+内存模型 ### 一个简简单单的单例设计模式之懒汉,就会涉及到双重检查锁定DCL以及指令重排带来的安全隐患并且设计到了内存模式。 ### 重点需要了解DCL、指令重排以及volatile关键字和new一个对象的整个过程 ### 直接看下面的代码,里面的注释写的非常详细 ### ~~~ package com.goldnecis.设计模式.单例模式.design; //1、单例模式(懒汉模式) //利用DCL(Double-Checked Locking)双重检查锁定是一种用于多线程环境下延迟初始化的优化技术,再结合懒加载(Lazy Initialization)和单例模式(Singleton Pattern)实现了最终可满足多线程环境下的单例模式 public class SingletonLazy { //4.7、为什么要使用static?因为getInstance是static,至于getInstance为什么是static下面有讲解,在static方法当中只能使用静态成员变量和静态方法,除非你实例化一个对象!所以instance也是static //在4.6当中提到的new的时候在多线程环境下有可能会发生指令重排,所以我们使用volatile关键字来修饰 所以得重点了解volatile关键字的作用 //在Java中,使用volatile关键字来修饰变量可以实现多线程之间的数据共享。当一个变量被声明为volatile时,它将具有以下特点: //可见性:对一个volatile变量的写操作会立即被其他线程可见,即保证了修改的值对其他线程是可见的。 //有序性:对于volatile变量的读写操作,编译器和处理器都会进行相应的指令重排,保证了指令的执行顺序。 //这里我们正是利用了volatile关键字可以防止指令重排的特性避免被实例化出多个对象 private static volatile SingletonLazy instance; //2、构造方法私有化 防止在外部进行实例化 只能在单例类的内部实例化返回 private SingletonLazy(){} //3、单例对象的方法 可理解为处理各种业务逻辑 供外界调用 public void process(){ System.out.println("我是单例设计模式"); } //4、供外界调用生成单例对象的方法 只能是静态方法 通过类名称来调用 因为构造方法当中禁止外界实例化单例类 //4.4、有可能你会问为什么不对getInstance方法加synchronized呢?因为这样锁的粒度太大,并发场景下肯定影响性能,不如减小锁的粒度控制到方法体内部的具体逻辑当中去 public static SingletonLazy getInstance(){ //4.1、第一重检查 避免不必要的同步 if(instance == null){ //4.3、因为在多线程环境下A B 锁定同时进来有可能会实例化多个对象,所以我们通过synchronized来实现线程同步以防止被实例化出来多个对象 synchronized (SingletonLazy.class){ //4.5、第二重检查 再次判断instance是否为空 确保只有一个线程实例化对象 if(instance == null){ //4.6、instance = new SingletonLazy(); 并不是原⼦性操作 // * ①、分配空间给对象 // * ②、在空间内创建对象 // * ③、将对象赋值给引⽤instance // 假如线程 1-3-2顺序(不要以为就是按照123的顺序进行new实例化,jvm虚拟机在编译的时候会进行指令重拍有可能是132),会把值写会主内存,其他线程就会读取到instance最新的值,但是这个是不完全的对象就会导致错误 // 我们该如何避免指令重拍呢?java当中new一个对象也有几率发生指令重排!!! // java已经为我们提供了完整的解决方案就是volatile关键字 去看4.7 instance = new SingletonLazy(); } } } //4.2、如果已经存在则直接返回该单例实例对象 return instance; } } ~~~