简体   繁体   English

DCL时需要volatile关键字

[英]Need of volatile keyword in case of DCL

I was just reading concurrency in practice. 我只是在实践中阅读并发性。 I came to know it is necessary to use volatile keyword in double checked locking mechanism for field otherwise thread can read stale value of not null object. 我知道有必要在字段的双重检查锁定机制中使用volatile关键字,否则线程可以读取非null对象的陈旧值。 Because it is a possibility of reordering instruction without use of volatile keyword. 因为不使用volatile关键字就可以对指令重新排序。 Because of that object reference could be assigned to resource variable before calling constructor. 因此,在调用构造函数之前,可以将引用分配给资源变量。 so thread could see partially constructed object. 因此线程可以看到部分构造的对象。

I have a question regarding that. 我对此有疑问。

I assume synchronized block also restricts compiler from instruction reordering so why we need volatile keyword here? 我认为同步块也限制了编译器对指令的重新排序,所以为什么我们在这里需要volatile关键字?

public class DoubleCheckedLocking {
    private static volatile Resource resource;
    public static Resource getInstance() {
        if (resource == null) {
            synchronized (DoubleCheckedLocking.class) {
                if (resource == null)
                    resource = new Resource();
            }
        }
        return resource;
    }
}

The JMM only guarantees that a thread T1 will see a properly initialized object created by another thread T2 inside a synchronized block if the calling thread (T1) also reads it from a synchronized block (on the same lock). 如果调用线程(T1)也从同步块(在同一锁上)读取线程,则JMM仅保证线程T1将在同步块内看到由另一个线程T2创建的正确初始化的对象。

Since T1 could see the resource as not null, and thus return it immediately without going though the synchronized block, it could get an object but not see its state properly initialized . 由于T1可以看到资源不为null,因此无需经过同步块就可以立即返回它,因此它可以获取一个对象,但看不到其状态正确初始化

Using volatile brings back that guarantee, because there is a happens-before relationship between the write of a volatile field and the read of that volatile field. 使用volatile可以带来保证,因为在volatile字段的写入与该volatile字段的读取之间存在事前发生的关系。

Volatile is necessary in this case, as others have observed, because a data race is possible when first accessing the resource. 正如其他人所观察到的那样,在这种情况下必须具有可变性,因为在首次访问资源时可能发生数据争用。 There is no guarantee, absent volatile , that thread A , reading a non-null value, will actually access the fully initialized resource -- if it is, at the same time, being built in thread B within the synchronized section, which thread A has not yet reached. 在没有volatile ,无法保证读取非空值的线程A实际上会访问完全初始化的资源-如果同时在同步部分的线程B中构建了该线程A,尚未达到。 Thread A could then try to work with a half-initialized copy. 然后,线程A可以尝试使用半初始化的副本。

Double-checked locking with volatile, while working since JSR-133 (2004) , is still not recommended, as it is not very readable and not as efficient as the recommended alternative : 从JSR-133(2004)开始使用时 ,仍然不建议使用volatile进行双重检查锁定,因为它不易读,并且不如推荐的替代方法有效

private static class LazyResourceHolder {
  public static Resource resource = new Resource();
}

...

public static Resource getInstance() {
  return LazyResourceHolder.something;
}

This is the Initialize-On-Demand Holder Class idiom, and according to the above page, 这是按需初始化Holder Class习惯用法,根据上一页,

[...] derives its thread safety from the fact that operations that are part of class initialization, such as static initializers, are guaranteed to be visible to all threads that use that class, and its lazy initialization from the fact that the inner class is not loaded until some thread references one of its fields or methods. [...]它的线程安全性来自以下事实:保证属于类初始化的一部分的操作(例如静态初始化程序)对于使用该类的所有线程都是可见的;其惰性初始化是由于内部类的事实在某些线程引用其字段或方法之一之前不会加载。

Actually there is no need to use volatile here. 实际上,这里不需要使用volatile Using volatile will mean that multiple threads will each time the instance variable is used in a thread method it will not optimize the memory read away but make sure it is read again and again. 使用volatile将意味着每次在线程方法中使用实例变量时都会有多个线程,这不会优化读取的内存,但要确保一次又一次地读取它。 The only times I've deliberately used volatile is in threads where I have a stop indicator ( private volatile boolean stop = false; ) 我唯一使用volatile是在线程中有停止指示符的地方( private volatile boolean stop = false;

Creating singletons like in your sample code is needlessly complex and doesn't offer any actual speed improvements. 像您的示例代码中那样创建单例会变得非常复杂,并且不会真正提高速度。 The JIT compiler is very good at doing thread locking optimizations. JIT编译器非常擅长进行线程锁定优化。

You'll be better out creating singletons using: 使用以下方法创建单例会更好:

public static synchronized Resource getInstance() {
    if (resource == null) {
        resource = new Resource();
    }
    return resource;
}

Which is much easier to read and infer its logic for human beings. 这更容易阅读并推断其对人类的逻辑。

See also Do you ever use the volatile keyword in Java? 另请参见您是否曾经在Java中使用volatile关键字? , where volatile is indeed generally used for some end-of-loop flag in threads. ,而volatile实际上通常用于线程中的某些循环结束标志。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM