why using volatile with synchronized block?
Asked Answered
P

5

53

I saw some examples in java where they do synchronization on a block of code to change some variable while that variable was declared volatile originally .. I saw that in an example of singleton class where they declared the unique instance as volatile and they sychronized the block that initializes that instance ... My question is why we declare it volatile while we synch on it, why we need to do both?? isn't one of them is sufficient for the other ??

public class SomeClass {
    volatile static Object uniqueInstance = null;

    public static Object getInstance() {
        if (uniqueInstance == null) {
            synchronized (someClass.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new SomeClass();
                }
            }
        }
        return uniqueInstance;
    }
}

thanks in advance.

Prosser answered 12/3, 2012 at 10:35 Comment(1)
What is "volatile static uniqueInstance = null;" ?Poltroonery
U
25

Synchronization by itself would be enough in this case if the first check was within synchronized block (but it's not and one thread might not see changes performed by another if the variable were not volatile). Volatile alone would not be enough because you need to perform more than one operation atomically. But beware! What you have here is so-called double-checked locking - a common idiom, which unfortunately does not work reliably. I think this has changed since Java 1.6, but still this kind of code may be risky.

EDIT: when the variable is volatile, this code works correctly since JDK 5 (not 6 as I wrote earlier), but it will not work as expected under JDK 1.4 or earlier.

Unsung answered 12/3, 2012 at 10:39 Comment(11)
you say that Synchronization would be enough if the first check was within synchronized block .... but I make the same check again after entering the synch block, so for sure the next thread will see the updated value of the variable.Prosser
Each thread may see a different snapshot, so the fact that one thread reads a variable inside a synchronized block does not mean that another thread which reads the variable without synchronization will see the same state. State will be consistent between threads only if they all use proper synchronization. As I mentioned, volatile takes care of that in your case from Java 5 upwards (i.e. volatile and synchronized not only cause a memory barrier each, but they also use the same barrier).Gynecocracy
Just as a heads-up to future readers: the article linked above is way out of date and as the edit states, the technique works fine on JDK 5 which was released almost 10 years ago.Opinionative
@MohammadDorgham @MichałKosmulski The second null check would always see the updated value. So does that mean the volatile is used only to fail the first null check for efficiency? (without volatile it would be slow but still correct?)Dumanian
From Oracle documentation "when a synchronized method exits, it automatically establishes a happens-before relationship with any subsequent invocation of a synchronized method for the same object. This guarantees that changes to the state of the object are visible to all threads" docs.oracle.com/javase/tutorial/essential/concurrency/…. I think it will be the case for synchronized block as well. If true then the blocked thread should see the updated value of "uniqueInstance".Premedical
@WeishiZeng Yes, it's just an optimizationGynecocracy
If I understand correctly, since Java 5, declaring uniqueInstance volatile is merely a performance improvement. Even if I don't declare it volatile, the code is still correct, right?Jarita
@MarkoCain No, you actually need the volatile. Without it, the first null check (the one outside synchronized block) is not synchronized and may give wrong resultsGynecocracy
@MichałKosmulski actually from a logical point of view, we do not need to declare it volatile, because the first null check might not be updated then, but it will return correctly at the second null check within the synchronized block (inside synchronized we have the up-to-date value). However, we need the volatile because it seems Java allows the publication of partially initialized objects, when volatile is not used, which means that a second thread could end up using an object which is not completely initialized. Volatile ensures that the reference points to a fully initialized object.Benfield
Besides volatile, it seems there are also other ways to ensure a variable is correctly published and not partly initialized, e.g. by using a final variable. More details can be found here: wiki.sei.cmu.edu/confluence/display/java/…Benfield
Correct @Benfield Everything within "syncronized" is guaranteed to be visible for other threads when the lock is released at the end. But while inside, instructions may be reordered. Therefore, because the 2nd thread checks uniqueInstance before entering "syncronized", it may see uncompleted object (object created and variable assigned, but constructor still in progress).Astronomical
E
7

This uses the double checked locking, note that the if(uniqueInstance == null) is not within the synchronized part.

If uniqueInstance is not volatile, it might be "initialized" with a partially constructed object where parts of it isn't visible to other than the thread executing in the synchronized block. volatile makes this an all or nothing operation in this case.

If you didn't have the synchronized block, you could end up with 2 threads getting to this point at the same time.

if(uniqueInstance == null) {
      uniqueInstance = new someClass(); <---- here

And you construct 2 SomeClass objects, which defeats the purpose.

Strictly speaking, you don't need volatile , the method could have been

public static someClass getInstance() {
    synchronized(FullDictionary.class) {
         if(uniqueInstance == null) {
             uniqueInstance = new someClass();
          }
         return uniqueInstance;
    }
}

But that incurs the synchronization and serialization of every thread that performs getInstance().

Empedocles answered 12/3, 2012 at 10:44 Comment(10)
I did the synchronization after the if statement to reduce the cost of synchronization, so my question is why I need to make it volatile while I double check after entering the synch block? I mean synch will update all shared variables immediately, so the next thread will see the updated value of uniqueInstanceProsser
@user1262445 As I said, without volatile a thread can see the object as partially constructed. So it will never enter the synchronized block, but return an partially constructed object if uniqueInstance is not volatile. I.e. you have 1 thread not entering the synch block and it might return garbage as another thread is right in the middle of the synchornized block.Empedocles
My question is without volititle, indeed the first if(uniqueInstance == null) { may be pass even though it is initialized , but after it enter the synchronized block, the miss judgement will be fixed since it has been synchronized so if(uniqueInstance == null) will be false and no new instance will be created, isn't it? So even without valitle , the code will still work as expected(only one same instance created and return ). But indeed has poorer performance? Am I right?Retrogress
@Empedocles I'm having the same doubt as @Jaskey. " New object is assigned to the variable only when the constructor return. And the default value for uniqueInstance is null. So inside synchronized block, how could it be a partially initialized object getting assigned to uniqueInstance ?Dumanian
@WeishiZeng What makes it not ? The compiler and runtime might 1. allocate the memory for your object, 2. assign that memory to uniqueInstance. 3. Run the constructor. Now, inbetween step 2 and 3, another thread could execute if(uniqueInstance == null) { which would be false, but uniqueInstance isn't finished being constructed, since step 3 isn't yet finished. There are other cases you can substitute for step 2 and 3 as well, dealing with memory visibility within the JVM.Empedocles
@Empedocles thanks for the reply!! Yes this is getting close to my question. For the order of step #2 and #3, do you know where is it documented? uniqueInstance = new someClass(); It seems to me that the object is assigned to the uniqueInstance only when the constructor return. (As my understanding of this SO stackoverflow.com/questions/7187842/…)Dumanian
@WeishiZeng It isn't documented explicitly, it's among many things, a consequence of what is not documented, thus leaving opportunities for an implementation to do optimization - but you can start by reading about the Java memory model in JSR-133. The question you link to is correct when you only consider 1 thread, that thread will naturally see the variable beeing assigned to after the constructor is run. But that might not be what another thread sees, if it had access to the same variable, without any locking.Empedocles
@Empedocles I've read thru the memory model at docs.oracle.com/javase/specs/jls/se8/jls8.pdf. But couldn't find direct answer. Could you kindly take a look at this question? (stackoverflow.com/questions/7187842/…) All the answers basically states: "the object is assigned to the variable only when the constructor return". Which means step#3 is before #2. Are they all wrong??Dumanian
@WeishiZeng It is a consequence of what is stated in chapter 17.4. The question you link to does not mention multi threading, see what I wrote about that in the previous comment.Empedocles
Let us continue this discussion in chat.Dumanian
I
6

This post explains the idea behind volatile.

It is also addressed in the seminal work, Java Concurrency in Practice.

The main idea is that concurrency not only involves protection of shared state but also the visibility of that state between threads: this is where volatile comes in. (This larger contract is defined by the Java Memory Model.)

Infrangible answered 12/3, 2012 at 10:43 Comment(0)
P
0

You can do synchronization without using synchronized block. It's not a necessary to use volatile variable in it... volatile updates the one variable from main memory..and synchronized Update all shared variables that have been accessed from main memory.. So you can use it according to your requirement..

Penates answered 12/3, 2012 at 10:46 Comment(0)
O
-2

My two cents here

Frist a quick explanation of the intuition of this code

if(uniqueInstance == null) {
        synchronized(someClass.class) {
            if(uniqueInstance == null) {
                uniqueInstance = new someClass();
            }
        }
    }

The reason it checks uniqueInstance == null twice is to reduce the overhead of calling the synchronized block which is relatively slower. So called double-checked locking.

Second, the reason it uses synchronized is easy to understand, it make the two operations inside the synchronized block atomic.

Last the volatile modifier makes sure all threads see the same copy so the very first check outside of the synchronized block will see the value of uniqueInstance in a way which is "synchronized" with the synchronized block. Without the volatile modifier one thread can assign a value to uniqueInstance but the other thread may not see it by the first check. (Although the second check will see it)

Outmaneuver answered 14/5, 2015 at 7:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.