在Java程序中,有时候可能需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化 。这称为延迟初始化或懒加载
看一个不安全的延迟初始化:
A线程执行1后,发现对象instance为null,准备对其new,而B线程却先new了,这造成了错误
我们可以利用同步锁,保证正确:
但是对整个方法进行同步开销太大,人们想出了双重检查锁定:
最小范围所用同步锁,利用双重检查看似实现了目的,但这出现了一个问题:当A线程4执行时,线程B的7还未执行完成,而线程A判定instance != null. 线程B的7还未执行完成,为什么会出现这种情况?
看一下new Instance()的底层关键实现:
其实是先执行1分配内存,然后再初始化对象和设置instance.然后这里存在重排,2和3的顺序可能被调换:
所以当B还执行完7时,A在4判定instance对象已经完成初始化了,如果在ctorInstance(memory)之前去调用instance就会出错。
解决办法有两个:
1.将instance对象声明为volatile,它会禁止2,3的重排
2.利用基于类初始化的解决方案 :JVM在类的初始化阶段(即在Class被加载后,且被线程使用之前),会执行类的初始化。在
执行类的初始化期间,JVM会去获取一个锁。这个锁可以同步多个线程对同一个类的初始化
我们会发现基于类初始化的方案的实现代码更简洁。但基于volatile的双重检查锁定的方案有一个额外的优势:除了可以对静态字段实现延迟初始化外,还可以对实例字段实现延迟初始化。字段延迟初始化降低了初始化类或创建实例的开 销,但增加了访问被延迟初始化的字段的开销。在大多数时候,正常的初始化要优于延迟初始化。如果确实需要对实例字段使用线程安全的延迟初始化,请使用上面介绍的基于volatile的延迟初始化的方案;如果确实需要对静态字段使用线程安全的延迟初始化,请使用上面介绍的基于类初始化的方案。
总结
以上所述是小编给大家介绍的java双重检查锁定的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对小牛知识库网站的支持!
问题内容: 我偶然遇到了一篇文章,该文章最近讨论了Java中的双重检查锁定模式及其陷阱,现在我想知道我多年来使用的那种模式的变体是否会遇到任何问题。 我看过许多关于该主题的文章和文章,并了解了对部分构造的对象的引用所带来的潜在问题,据我所知,我认为我的实现不受这些问题的影响。以下模式是否有问题? 而且,如果没有,人们为什么不使用它?在围绕此问题进行的任何讨论中,我从未见过推荐它的方法。 问题答案:
我使用Java大约一个月了,在编程方面仍然是一个一般的业余爱好者,所以如果我有什么不对的地方,请随时纠正我。也许我会提供一些多余的细节,但我现在是如此困惑,我无法决定什么是重要的。 所以,我一直在开发多线程客户机-服务器应用程序。所有线程都在使用同一个对象,其中存储了某些配置值和共享记录器;此对象在server-thread中初始化,然后作为参数传递给client-thread类构造函数。首先,假
这是我为单例模式的自定义类。在这段代码中,我使用双重检查锁定,如下所示。当我在一些源代码上读到许多帖子时,他们说双重检查很有用,因为它可以防止两个并发线程同时运行,从而产生两个不同的对象。 我还是不太懂上面的代码。当实例为null时,如果两个线程一起运行同一行代码,问题是什么? 当它出现时。两个线程都将看到对象为null。然后两者同步。然后,他们再次检查,仍然看到它为空。并创建两个不同的对象。哎呀
据我所知,这是Java中双重检查锁定模式的正确实现(自Java5): 我想知道缺少是一个严重的错误,还是一个可能存在性能缺陷的轻微缺陷,假设只能通过访问。 我的想法是这样的:引入了事前发生关系。初始化的线程将其值写入离开同步块的主存储器。因此,即使不是易失性的,也不会有的双重初始化:其他线程的本地副本中有(在第一次检查中得到),但是必须在进入同步块后的第二次检查中从主存储器中读取新值(get并且不
问题内容: 此问题与旧Java版本的行为以及双重检查锁定算法的旧实现有关 较新的实现使用volatile并依赖于略有更改的语义,因此它们 不会 损坏。 声明字段分配始终是原子的,除了long或double字段。 但是,当我阅读解释为什么双重检查锁定被破坏时,据说问题出在赋值操作中: 线程A注意到该值未初始化,因此它获得了锁并开始初始化该值。 由于某些编程语言的语义,允许编译器生成的代码在A完成执行
本文向大家介绍Java线程安全的Singleton具有双重检查的锁定,包括了Java线程安全的Singleton具有双重检查的锁定的使用技巧和注意事项,需要的朋友参考一下 示例 这种类型的Singleton是线程安全的,并且可以在创建Singleton实例之后防止不必要的锁定。 Java SE 5 必须强调的是-在Java SE 5之前的版本中,上述实现是错误的,应避免使用。在Java 5之前的J
问题内容: 在设计模式手册中,具有双重检查锁定的单例模式已实现如下: 我不明白为什么要使用它。使用不会 违反使用双重检查锁定(即性能)的目的吗? 问题答案: 真正的问题是可能在完成构造之前为其分配存储空间。将看到该作业并尝试使用它。由于使用的是的部分构造版本,因此导致失败。
本文向大家介绍Java双重检查加锁单例模式的详解,包括了Java双重检查加锁单例模式的详解的使用技巧和注意事项,需要的朋友参考一下 什么是DCL DCL(Double-checked locking)被设计成支持延迟加载,当一个对象直到真正需要时才实例化: 为什么需要推迟初始化?可能创建对象是一个昂贵的操作,有时在已知的运行中可能根本就不会去调用它,这种情况下能避免创建一个不需要的对象。延迟初始化