当前位置: 首页 > 面试题库 >

为什么Java中双重检查锁定被破坏?

曹兴贤
2023-03-14
问题内容

此问题与旧Java版本的行为以及双重检查锁定算法的旧实现有关

较新的实现使用volatile并依赖于略有更改的volatile语义,因此它们 不会 损坏。

声明字段分配始终是原子的,除了long或double字段。

但是,当我阅读解释为什么双重检查锁定被破坏时,据说问题出在赋值操作中:

// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo {
    private Helper helper = null;
    public Helper getHelper() {
        if (helper == null) {
            synchronized(this) {
                if (helper == null) {
                    helper = new Helper();
                }
            }
        }
        return helper;
    }

    // other functions and members...
}
  1. 线程A注意到该值未初始化,因此它获得了锁并开始初始化该值。
  2. 由于某些编程语言的语义,允许编译器生成的代码在A完成执行初始化之前将共享变量更新为指向部分构造的对象。
    3.
    线程B注意到共享变量已初始化(或出现),并返回其值。因为线程B认为该值已被初始化,所以它不获取锁。如果B在A看到由A完成的所有初始化之前就使用了该对象(要么是因为A尚未完成对其的初始化,要么是因为该对象中的某些初始化值尚未渗透到B使用的内存中(缓存一致性))
    ,程序可能会崩溃。
    (来自http://en.wikipedia.org/wiki/Double-
    checked_locking)。


什么时候有可能?在64位JVM上分配操作是否可能不是原子的?如果否,那么“双重检查锁定”是否真的无效?


问题答案:

问题不是原子性,而是排序。JVM是允许对指令重新排序,以提高性能,只要之前发生不受侵犯。因此,运行时理论上可以在执行helper来自类构造函数的所有指令之前安排要更新的指令Helper



 类似资料:
  • 问题内容: 我偶然遇到了一篇文章,该文章最近讨论了Java中的双重检查锁定模式及其陷阱,现在我想知道我多年来使用的那种模式的变体是否会遇到任何问题。 我看过许多关于该主题的文章和文章,并了解了对部分构造的对象的引用所带来的潜在问题,据我所知,我认为我的实现不受这些问题的影响。以下模式是否有问题? 而且,如果没有,人们为什么不使用它?在围绕此问题进行的任何讨论中,我从未见过推荐它的方法。 问题答案:

  • 问题内容: 在设计模式手册中,具有双重检查锁定的单例模式已实现如下: 我不明白为什么要使用它。使用不会 违反使用双重检查锁定(即性能)的目的吗? 问题答案: 真正的问题是可能在完成构造之前为其分配存储空间。将看到该作业并尝试使用它。由于使用的是的部分构造版本,因此导致失败。

  • 我使用Java大约一个月了,在编程方面仍然是一个一般的业余爱好者,所以如果我有什么不对的地方,请随时纠正我。也许我会提供一些多余的细节,但我现在是如此困惑,我无法决定什么是重要的。 所以,我一直在开发多线程客户机-服务器应用程序。所有线程都在使用同一个对象,其中存储了某些配置值和共享记录器;此对象在server-thread中初始化,然后作为参数传递给client-thread类构造函数。首先,假

  • 据我所知,这是Java中双重检查锁定模式的正确实现(自Java5): 我想知道缺少是一个严重的错误,还是一个可能存在性能缺陷的轻微缺陷,假设只能通过访问。 我的想法是这样的:引入了事前发生关系。初始化的线程将其值写入离开同步块的主存储器。因此,即使不是易失性的,也不会有的双重初始化:其他线程的本地副本中有(在第一次检查中得到),但是必须在进入同步块后的第二次检查中从主存储器中读取新值(get并且不

  • 可变一的示例和说明如下: 它不起作用的第一个原因 它不起作用的最明显的原因是初始化Helper对象的写操作和对Helper字段的写操作可能不按顺序进行或被感知。因此,调用getHelper()的线程可以看到对helper对象的非空引用,但看到的是helper对象字段的默认值,而不是构造函数中设置的值。 谢谢

  • 这是我为单例模式的自定义类。在这段代码中,我使用双重检查锁定,如下所示。当我在一些源代码上读到许多帖子时,他们说双重检查很有用,因为它可以防止两个并发线程同时运行,从而产生两个不同的对象。 我还是不太懂上面的代码。当实例为null时,如果两个线程一起运行同一行代码,问题是什么? 当它出现时。两个线程都将看到对象为null。然后两者同步。然后,他们再次检查,仍然看到它为空。并创建两个不同的对象。哎呀