1

Reading about DCL in Wikipedia, I was wondering about the problem in DCL and the proposed solution, or in other words why is the volatile keyword needed? The problem, in short: using DCL may result in some cases in a partially constructed object being used by thread. The proposed solution (for JAVA 5+):

// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
    private volatile Helper helper;
    public Helper getHelper() {
        Helper result = helper;
        if (result == null) {
            synchronized(this) {
                result = helper;
                if (result == null) {
                    helper = result = new Helper();
                }
            }
        }
        return result;
    }

    // other functions and members...
}

Now, my question is why not drop the volatile from helper? correct me if I'm wrong, but if you just break this line: helper = result = new Helper(); to:

result = new Helper();
helper = result;

In this scenario the helper will never get the reference until the object is complete. Isn't that so? How is the volatile doing better?

EDIT: assume this code:

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

If the next line after initialization doesn't guaranty a fully initialized object, I can't call methods on it. Can I?

Yekhezkel Yovel
  • 205
  • 1
  • 2
  • 10
  • Because `result = new Helper(); helper = result;` is exactly the same as `helper = result = new Helper();`. What did you think would be different? – user253751 May 25 '14 at 09:13
  • 4
    Apparently, you assume that all actions will be executed in a total order that is consistent with program order. That's not true for multi-threaded programs in general. – nosid May 25 '14 at 09:17
  • @nosid, I realize that the initialization line may receive the address before initialization is complete, but I was assuming that the next line will only be executed after initialization is complete. Otherwise, how can I even use an object after I instantiate it? I can never now if it is fully initialized? See EDIT. – Yekhezkel Yovel May 25 '14 at 09:23
  • @YekhezkelYovel: The actions within a single thread will always _appear_ to be executed in program order. Regarding multiple threads, the execution will _appear_ to be [sequentially consistent](http://en.wikipedia.org/wiki/Sequential_consistency), if there are no data races. Your code contains a data race on `helper`, if the variable isn't `volatile`. – nosid May 25 '14 at 09:37
  • @YekhezkelYovel: In the second listing, where is the variable `helper` updated? – nosid May 25 '14 at 09:38
  • it isn't. The point of the EDIT is to explain why I think the first listing should work. If you can call a function on `result` in that line, than it should be fully initialized and ready to be assigned to `helper` as well. I think. – Yekhezkel Yovel May 25 '14 at 09:56
  • But I added it anyway. @nosid – Yekhezkel Yovel May 25 '14 at 09:58
  • @YekhezkelYovel: If you do not write to `helper`, there is no problem. That might be surprising. Maybe the following helps: On a multi-core CPU, each core has its own cache. Write operations to one cache might be propagated to another cache in a different order. So, a thread running on another core might see the update to `helper` before the updates to the member variables of the newly constructed object, although they were made in the correct order. – nosid May 25 '14 at 10:28

1 Answers1

2

If you omit volatile, the compiler is free to optimize out result completely. This extra local variable doesn't change that much. It saves an extra load, but it's the volatile keywords which fixes the race condition.

Let's assume we have following fragment of code:

public volatile Object helper;

public synchronized void initHelper() {
    Object result = new Object();
    helper = result;
}

It will be compiled to following pseudo-assembly:

public synchronized void initHelper() {
    Object result = Object.new();
    result.<init>();
    // result is now fully initialized
    // but helper is still null for all threads
    this.helper = result;
    // now all threads can access fully initialized helper
}

If you remove volatile the compiler is allowed to assume no other thread uses helper and rearrange the code to optimize it. It might decide to remove unnecessary local variable and produce following output:

public synchronized void initHelper() {
    this.helper = Object.new();
    // from now on other threads can access helper
    this.helper.<init>();
    // but it's fully initialized only after this line
}

If you add a function call in initHelper. The compiler will never put this call before initialization. That would change the meaning of the program even in single thread. But it's allowed to do optimizations which will break multithreaded code accessing this field.

Piotr Praszmo
  • 17,928
  • 1
  • 57
  • 65
  • Thanks @Banthar. It give a better understanding of `volatile` even though I'm still a bit confused. See my EDIT please. – Yekhezkel Yovel May 25 '14 at 09:30
  • Thanks @Banthar. What will it look like if I use the `volatile` keyword but do not use a local variable? – Yekhezkel Yovel May 25 '14 at 10:26
  • The transformation is not correct, because `` might throw an exception. – nosid May 25 '14 at 10:31
  • In this case it won't change anything. The compiler will add a temporary variable on its own so initialization is done before assignment to field. `helper` was assigned to only once - compiler is not allowed to change that. – Piotr Praszmo May 25 '14 at 10:33
  • @nosid you are right. This example is a gross simplification. Maybe I shouldn't call this `` as this is not really the constructor. – Piotr Praszmo May 25 '14 at 10:35