Need of volatile keyword in case of DCL

为君一笑 提交于 2019-12-24 00:57:57

问题


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. Because it is a possibility of reordering instruction without use of volatile keyword. 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?

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;
    }
}

回答1:


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).

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.

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.




回答2:


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. Thread A could then try to work with a half-initialized copy.

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:

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,

[...] 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.




回答3:


Actually there is no need to use volatile here. 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. The only times I've deliberately used volatile is in threads where I have a stop indicator (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.

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?, where volatile is indeed generally used for some end-of-loop flag in threads.



来源:https://stackoverflow.com/questions/49840773/need-of-volatile-keyword-in-case-of-dcl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!