简体   繁体   中英

Volatile and Safe Publication of objects

In few articles, it says that using of volatile fixes the Double Checked Locking issue.

class Foo {
        private volatile Helper helper = null;
        public Helper getHelper() {
            if (helper == null) {
                synchronized(this) {
                    if (helper == null)
                        helper = new Helper(); //Important
                }
            }
            return helper;
        }
    }

But here even if we use volatile for helper field, how can this be a safe publication ? I mean how could this guarantee that we won't get an in-consistence Helper object?

Without volatile, because the initial if (helper == null) is outside of a synchronized block, there is no visibility / consistency guarantee. In particular, it would be possible that helper would be not null but referred to an object that is only partially created.

That is because helper = new Helper() is not an atomic operation:

  1. it allocates some memory
  2. it constructs the object
  3. it assigns a reference to the object to helper

Without synchronization, an observing thread could see these operations in any order, in particular it could see 3 before 2.

By making helper volatile, you introduces a happens-before relationship (as defined by the Java Memory Model ) between the write and the read, which ensures that if you see 3 from an observing thread, you will also see 1 and 2.

In particular, any operations executed before the volatile write will be visible from the volatile read (if it is subsequent of course).

I think it's because the JVM preserves the order of the helper=new Helper() . I mean only after the creation of the object the assignment will happen. Please correct me if I'm wrong.

From a high-level perspective , volatile guarantees that result of any action that happened before volatile write (ie actions inside new Helper() ) will be visible to any other thread after subsequent volatile read (ie when another thread sees helper != null in the outer check).

From a low-level perspective , it's implementation dependent. But in general:

  • Compiler optimizations that may cause reorder of volatile write with previous actions should be prohibited
  • JIT should insert memory barriers into generated code to prevent similar reorder at CPU level (if particular CPU architectures allows such a reorder)

Ok, the check outside the synchronized section is to avoid needless entering of the monitor section. It could be omitted if you are trying to optimize performance. It also only works if there is no way to reset helper to null after creaton of the object. The check inside the synchronized section is to avoid inconsistence (as one could have created the object while we were waiting for entering the monitor).

The inner part of synchronized can only be executed mutual exclusively That's where the consistency comes from. But again: The outer if (helper == null) is only allowed if helper can't turn null anymore after being assigned with a new reference to an instance of Helper.

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