Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Is volatile / final required for reference to synchronized object?

This seems a pretty basic issue, but I cannot find a clear confirmation.

Let's say I have a class properly synchronized in itself:

public class SyncClass {

   private int field;

   public synchronized void doSomething() {
       field = field * 2;
   }

   public synchronized void doSomethingElse() {
       field = field * 3;
   }
}

If I need to have a reference to an instance of that class, shared between threads, I do still need to declare that instance volatile or final, am I right? As in:

public class MainClass { // previously OuterClass

    public static void main(String [ ] args) {

        final SyncClass mySharedObject = new SyncClass();

        new Thread(new Runnable() {
            public void run() {
                mySharedObject.doSomething();
            }
       }).start();

       new Thread(new Runnable() {
            public void run() {
                mySharedObject.doSomethingElse();
            }
       }).start();
    }
}        

Or, if mySharedObject cannot be final, because its instantiation depends on some other conditions (interaction with GUI, info from socket, etc.), not known beforehand:

public class MainClass { // previously OuterClass

    public static void main(String [ ] args) {

        volatile SyncClass mySharedObject;

        Thread initThread = new Thread(new Runnable() {
            public void run() {

            // just to represent that there are cases in which
            //   mySharedObject cannot be final
            // [...]
            // interaction with GUI, info from socket, etc.
            // on which instantation of mySharedObject depends

            if(whateverInfo)
                mySharedObject = new SyncClass();
            else
               mySharedObject = new SyncClass() {
                   public void someOtherThing() {
                     // ...
                   }
               }
            }
       });

       initThread.start();

       // This guarantees mySharedObject has been instantied in the
       //  past, but that still happened in ANOTHER thread
       initThread.join();

       new Thread(new Runnable() {
            public void run() {
                mySharedObject.doSomething();
            }
       }).start();

       new Thread(new Runnable() {
            public void run() {
                mySharedObject.doSomethingElse();
            }
       }).start();
    }
}        

Final or volatile are mandatory, the fact that MyClass synchronizes the access to its own members, does NOT exempt to take care in ensuring that the reference is shared among threads. Is that right?

Differences with Difference between volatile and synchronized in Java

1- The referred question is about synchronized and volatile as alternatives, for the same field/variable, my question is about how to correctly use an already properly synchronized class (i.e. synchronized has been choosen), considering implications needed to be considered by the caller, possibly using volatile/final on a reference of an already synchronized class.

2- In other words, the referred question/answers are about locking/volatile THE SAME OBJECT, my question is: how can I be sure different threads actually SEE THE SAME OBJECT? BEFORE locking/accessing it.

When the first answer of referred question refers explicitly to a volatile reference, it's about an immutable object without synchronization. The second answer limits itself to primitive types. I DID find them useful (see below), but not complete enough to shed any doubts on the case I'm giving here.

3- The referred answers are very abstract and scholarly explanations to a very open question, with quite no code at all; as I stated in the introduction I need to a clear confirmation to actual code referring a specific, while quite common, issue. They're related, sure, but just as a text book is related to a specific problem. (I actually read it before opening this question, and find it useful, yet I still need to discuss a specific application.) If text books resolved all problems/doubts people may have applying them, we probably wouldn't need stackoverflow at all.

Consider that, in multithreading, you cannot "just try it out", you need a proper understanding and be sure of details, because race conditions can go right a thousand times and then go horribly wrong the thousand + 1 time.

like image 758
GozzoMan Avatar asked Mar 18 '15 18:03

GozzoMan


People also ask

Is volatile required with synchronized?

If it is accessed only from synchronized blocks is not needed the volatile keyword. Synchronized guarantees that changes to variables accessed inside the synchronized block are visible to all threads entering a synchronized block.

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

Does synchronization techniques are required for volatile variable?

Effectively, a variable declared volatile must have it's data synchronized across all threads, so that whenever you access or update the variable in any thread, all other threads immediately see the same value.

Is synchronized volatile?

Synchronized is applicable only on blocks or methods. Volatile is applicable to variables only. The synchronized modifier is used to implement a lock-based concurrent algorithm, i.e it suffers from the limitation of locking.


2 Answers

Yes you are right. It is necessary that you make access to the variable also thread-safe. You can do that either by making it final or volatile, or you ensure that all threads access that variable again inside a synchronous block. If you wouldn't do that, it might be that one thread 'sees' already the new value of the variable, but the other thread might still 'see' null, for example.

So regarding your example, you could sometimes get a NullPointerException when a thread accesses the mySharedObject variable. But this might only happen on multi-core machines with multiple caches.

Java Memory Model

The main point here is the Java Memory Model. It states a thread is only guaranteed to see a memory update of another thread if that update happens before the read of that state in the so-called happens-before relation. The happens-before relation can be enforced by using final, volatile, or synchronized. If you do not use any of these constructs a variable assignment by one thread is never guaranteed to be visible by any other thread.

You can think of threads to conceptually have local caches and as long as you do not enforce that caches of multiple threads are synchronized, a thread just reads and writes to its local cache. This might lead to the situation where two threads see completely different values when reading from the same field.

Note that there are some additional ways to enforce visibility of memory changes, for example, using static initializers. In addition, a newly created thread always sees the current memory of its parent thread, without further synchronization. So your example might even work without any synchronization, because the creation of your threads are somehow enforced to happen after the field has been initialized. However relying on such a subtle fact is very risky and can easily break if you later refactor your code without having that detail in mind. Further details about the happens-before relation are described (but hard to understand) in the Java Language Specification.

like image 79
Jan Schaefer Avatar answered Oct 28 '22 11:10

Jan Schaefer


If I need to have a refence to an instance of that class, shared between threads, I do still need to declare that instance volatile or final, am I right?

Yes, you are right. In this case you have two shared variables:

private int field

private SyncClass mySharedObject

Because of the way you have defined SyncClass any reference to a SyncClass will give you the most up to date value of that SyncClass.

If you don't synchronize the access to mySharedObject correctly (a non-final, non-volatile) field and you change the value of mySharedObject you may get a mySharedObject which is out of date.

like image 23
John Vint Avatar answered Oct 28 '22 12:10

John Vint