简体   繁体   中英

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

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.

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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