简体   繁体   English

非易失性双重检查锁定,可以吗?

[英]Non volatile double checked locking, is it possible?

Here is my singleton class. 这是我的单身人士班。

Static instance field is not volatile thus reordering/visibility problem arises. 静态instance字段不是易变的,因此出现了重新排序/可见性问题。 To solve it instance val field is made final. 为了解决它,实例val字段被设为final。 Since instance is properly constructed its clients should always see val field initialized if they see instance at all. 由于实例是正确构造的,因此如果它们的客户端完全看到实例,则它们的客户端应始终看到val域已初始化。

    static class Singleton {
    private static Singleton instance;
    private final String val;
    public Singleton() { this.val = "foo"; }

    public static Singleton getInstance() {
        if (instance == null)
            synchronized (Singleton.class) {
                if(instance == null) {
                    instance = new Singleton();
            }
        }
        return instance;
    }
    public String toString() { return "Singleton: " + val; }
}

However there is another problem - I've got two unprotected reads of "instance" field which can be(?) reordered so that client may get null instead of real value: 但是,还有另一个问题-我对“实例”字段进行了两个不受保护的读取,可以对它们进行重新排序(?),以便客户端可以获取null而不是实际值:

public static Singleton getInstance() {
    Singleton temp = instance;
    if (instance != null) return temp;
    else { /* init singleton and return instance*/ }
}

To workaround this I feel like I can introduce local variable: 要解决此问题,我觉得我可以引入局部变量:

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

This seem to solve the problem since there is only one unprotected read of value so nothing really evil should happen. 这似乎解决了问题,因为只有一个不受保护的价值读取,因此没有真正的邪恶发生。 But... I've just modified the program flow without(almost?) changing its single threaded semantics. 但是...我刚刚修改了程序流程,而没有(几乎完成?)更改其单线程语义。 Does this mean that compiler can just undo my workaround since this transformation is safe and there is no way to make this code working without establishing proper happens-before relationship with volatile? 这是否意味着编译器可以撤消我的解决方法,因为这种转换是安全的,并且如果不与volatile建立正确的事前关系,就无法使该代码正常工作?

I'm not sure whether a reordering of reads of the same variable really may occur, but it's guaranteed that local variables are unaffected by other thread's activities. 我不确定是否真的会发生对相同变量的读取的重新排序,但是可以确保局部变量不受其他线程活动的影响。 Even if such read reorderings do not happen, this guaranty is relevant for every variable which might be updated concurrently while you read it: if you read a value and store it into a local variable you can be sure that the value of the local variable does not suddenly change afterwards. 即使没有发生这种读取重新排序,此保证也适用于在读取时可能同时更新的每个变量:如果读取值并将其存储到局部变量中,则可以确保局部变量的值确实之后不会突然改变。 Of course, if the value is a reference, that guaranty does not apply to the fields of the referenced object. 当然,如果值是引用,则该保证不适用于引用对象的字段。

The relevant sentence can be found in the JLS §17.4.1 : 相关句子可以在JLS§17.4.1中找到:

Local variables (§14.4), formal method parameters (§8.4.1), and exception handler parameters (§14.20) are never shared between threads and are unaffected by the memory model. 局部变量(第14.4节),形式方法参数(第8.4.1节)和异常处理程序参数(第14.20节)永远不会在线程之间共享,并且不受内存模型的影响。

So the answer is no, a compiler is not allowed to undo your workaround of introducing a local variable. 因此答案是否定的,不允许编译器撤消引入局部变量的解决方法。

The safest way to do lazy-initialisation singletons is to use another class to hold the single instance field and rely on the guarantees the Java language provides for class initialisation 进行延迟初始化单例的最安全方法是使用另一个类来保存单个实例字段,并依靠Java语言为类初始化提供的保证

public class Singleton {
  private static class Holder {
    static final Singleton instance = new Singleton();
  }

  public Singleton getInstance() {
    return Holder.instance;
  }
}

The Holder class will only be initialised (and thus the instance created) the first time getInstance() is called. 仅在首次调用getInstance()时初始化Holder类(并因此创建实例)。

I don't think you have an issue from the beginning. 我认为您从一开始就没有问题。

You use synchronized(Singleton.class) . 您使用synchronized(Singleton.class) Upon synchronized Java guarantees any read/write before this keyword are readily reflected into memory for involved variables. synchronized Java会保证在此关键字容易反映到所涉及变量的内存之前进行任何读/写操作。 Since your Singleton instance is also declared at class level, any modification to it is readily visible from other class and is populated into main memory. 由于您的Singleton instance也是在类级别声明的,因此对其的任何修改都易于从其他类中看到,并填充到主内存中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 双重检查锁定没有挥发性 - Double-checked locking without volatile 为什么volatile用于double checked locking - Why is volatile used in double checked locking 如何打破双重检查锁定没有volatile - How to break double checked locking without volatile 为什么即使在波动的修复之后,双重检查锁仍然不鼓励? - why is double checked locking still discouraged even after the volatile fix? 在Java中进行双重检查锁定是否需要`volatile`而不是C#? - Is `volatile` required for double-checked locking in Java but not C#? 使用双重检查锁定,在保证之前是否发生了对易失性ConcurrentHashMap的放置? - With double-checked locking, does a put to a volatile ConcurrentHashMap have happens-before guarantee? 像这样初始化后,是否可以避免重复检查锁定中的易失性读取? - Can I avoid the volatile read in double checked locking once initialized like this? 仔细检查了锁定的懒惰初始化-我是否需要Map的volatile关键字 - Double checked locking lazy Initialization - Do I need to have volatile keyword for the Map 双重检查锁定而不使用volatile-keyword并且不同步整个getInstance()方法 - Double checked locking without using volatile-keyword and without synchronizing the entire getInstance() method 单例中的双重检查锁定 - Double Checked Locking in Singleton
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM