Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Differences in safe publishing between volatile,final and synchronized

Given a class A with variable x. Variable x is set in class constructor:

A() {
x = 77;
}

We want to publish x to some other thread. Consider the following 3 cases of variable x thread-safe (?) publication:

1) x is final

2) x is volatile

3) x is set in synchronized block

synchronized(someLock) {
A a  = new A();
a.x = 77;
}

Thread2 simply prints x:

 System.out.println(a.x);

The question is: is it possible to observe '0' printed by Thread2? Or it is guaranteed by JMM that '77' will be printed or NPE will be thrown in all 3 cases?

like image 880
MiamiBeach Avatar asked May 30 '14 17:05

MiamiBeach


People also ask

What is the difference between volatile and synchronized?

So where volatile only synchronizes the value of one variable between thread memory and "main" memory, synchronized synchronizes the value of all variables between thread memory and "main" memory, and locks and releases a monitor to boot. Clearly synchronized is likely to have more overhead than volatile.

Should I use volatile with synchronized?

When to use Volatile over Synchronized modifiers can be summed up into this: Use Volatile when you variables are going to get read by multiple threads, but written to by only one thread. Use Synchronized when your variables will get read and written to by multiple threads.

What is the difference between a synchronized method and a synchronized block?

Key DifferencesA synchronized method assigns an object-level or class-level corresponding lock. And synchronized block assign a lock to the object based on the parameter.

Do I need volatile in 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..


1 Answers

My answer comes from the best page on the internet, in particular chapter 17 that deals with memory visibility and concurrency.

I am also assuming that you do not have reference leaks (I.e. you do not have a reference to the object before the object constructor is finished).

  • Final field. I'll just quote the above page on this, chapter 17.5:

    An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

  • Volatile. Again, I will just quote the JLS:

A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order).

Hence, assuming your Thread got access to the object after it finished the constructor, it will see the correct value. Note, this means you will likely need to make a volatile as well.

  • x is in a synchronized block. This is a tricky one. It may or may not be visible. In fact, synchronization only slightly increases the difficulty of explaining this, so I will drop it and explain whether a simple local variable will be seen here or not. And then add a clause about synchronized.

By definition, it is guaranteed to be visible if there is a Happens-before relationship between the read and the write. Otherwise it may happen that you see an uninitialized value. What constitutes a Happens-before relationship? Again, the JLS chapter 17 specifies this, in particular:

  • Operation order in a single thread.
  • Synchronization, locks and volatility.
  • Object and Thread creation.
  • Everything is transitive.
  • More stuff, read the JLS

Hence there can be two situations:

A a = new A();
Thread t = new MyThread(a);
t.start();

Where MyThread saves the instance of A and uses that. In this case, the thread is created after a, and start() is called after it is created. Hence visibility is guaranteed, even if x is non-volatile. However, visibility of further changes to x is not guaranteed.

Situation 2:

This is a little more difficult to code up, but:
Main creates two Threads and immediately starts them, and has a single non-volatile field of type A.
ThreadA creates A and writes it to the shared field.
ThreadB loops through a while until the field is populated and then prints out x.

In this case, even though there is a HB between the write to x and the write to the shared field, there is no HB between read and write of the shared field. Hence there is no guarantee of the visibility of write to x.

As promised - if you put a synchronized block around the write to x in either of these 2 cases, it will not affect the outcome, since there is nothing else locking on the monitor. Locking and unlocking the same monitor creates a synchronization action, and hence create a HB relationship.

like image 121
Ordous Avatar answered Sep 20 '22 17:09

Ordous