锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

DCL单例模式

时间:2023-06-29 06:07:00 dcl光伏电抗器三相功率电感器

一、对象的创建过程

视频教程

对象的创建过程:

创建->初始化->建立连接

1.先申请内存,赋值默认值0

2.构造方法赋值初始值,8

三、建立连接,t->T

二、DCL单例

我们第一次写的单例模式如下:

public class Singleton {     private static Singleton instance = null;     public static Singleton getInstance() {         if(null == instance) {                    // line A             instance = new Singleton();        // line B         }          return instance;      } }

假设这样的场景:

并发调用两个线程Singleton.getInstance()假设线程先判断instance是否为null,即代码中line A进入到line B位置。判断后,JVM将CPU向线程二切换资源,

由于线程1尚未执行line B,所以instance还是空的,所以线程二执行了new Singleton()操作。过了一会儿,当线程再次被唤醒时,它仍然执行new Singleton()操作,

这样来了,new出了两个instance,这还能叫单例吗?

然后,我们再次尝试单例模式:

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

与第一段代码相比,方法中只有一个synchronized现在可以保证不会出线程问题。

但是这里有一个很大的性能问题(至少耗时很大)。

除第一次调用外,还执行了第一次调用Singleton除了构造函数,未来的每一次调用都将直接返回instance对象。

返回对象的操作时间很小,大部分时间都用于synchronized装饰符的同步准备不划算

继续将代码改为以下内容:

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

基本上,把synchronized移动到代码内部是没有意义的。每次调用getInstance()还是要进行同步。

同步本身没有问题,但我们只想第一次创建它instance例子同步,所以有以下写法——检查(DCL,Double Check Lock)

public class Singleton {     private static Singleton instance = null;     public  static Singleton getInstance() {         if(null == instance) {    // 检测到线程二instance不为空             synchronized (Singleton.class) {                 if(null == instance) {                     instance = new Singleton();    // 线程一被指示重排,首先执行执行,但结构函数(即初始化未完成)尚未完成。                 }             }         }          return instance;    // 执行后面的线程2将导致对象尚未初始化错误      } }

除了第一次创建对象外,其他访问似乎已经满足了要求。if回来,所以不会走到同步块,完美吗?

如上代码段注释:假设线程一执行到instance = new Singleton()这句话在这里看起来像一句话,但实际上是编译后的JVM执行对应的会变代码发现,这句话被编译成8个汇编指令,大致做了三件事:

1)给instance实例分配内存;

2)初始化instance的构造器;

3)将instance对象指向分配的内存空间(注意到这步时instance就非null了)

按顺序执行指令也没关系,但是JVM为了优化指令,提高程序运行效率,允许指令重新排序。这样,当程序真正运行时,上述指令执行顺序可能是这样的:

a)给instance实例分配内存;

b)将instance对象指向分配的内存空间;

c)初始化instance的构造器;

此时,当线程一执行时b)完成,执行c)此时,它被切换到线程2instance判断为非空,线程二直接来retrn instance语句,拿走instance然后使用,接着就顺理成章地报错(对象尚未初始化)。

  具体来说就是synchronized虽然保证了线程的原子性(即synchronized块中的语句要么全部执行,要么一条也不执行),但单条语句编译后形成的指令并不是一个原子操作(即可能该条语句的部分指令未得到执行,就被切换到另一个线程了)

  根据以上分析可知,解决这个问题的方法是:禁止指令重排序优化,即使用volatile变量

public class Singleton {
    private volatile static Singleton instance = null;
    public  static Singleton getInstance() {
        if(null == instance) {
            synchronized (Singleton.class) {
                if(null == instance) {
                    instance = new Singleton();
                }
            }
        }

        return instance;

    }
}

将变量instance使用volatile修饰即可实现单例模式的线程安全

参考博客、视频教程

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章