Java Threads: Should all shared variables be Volatile ? [duplicate]
Asked Answered
M

3

9

I am trying to understand how multithreading works in Java. I understand the difference between Volatile and Synchronization.

Volatile is about visibility and doesn't guarantee synchronization. When we are working with multithreading environments, each thread creates its own copy on a local cache of the variable they are dealing with. When this value is being updated, the update happens first in the local cache copy, and not in the real variable. Therefore, other threads are agnostic about the values that other threads are changing. And this is where volatile comes into picture. Volatile fields are immediately written through to main memory, and reads occur from main memory.

Snippet from Thinking In Java -

Synchronization also causes flushing to main memory, so if a field is completely guarded by synchronized methods or blocks, it is not necessary to make it volatile.

It’s typically only safe to use volatile instead of synchronized if the class has only one mutable field. Again, your first choice should be to use the synchronized keyword—that’s the safest approach, and trying to do anything else is risky.

But my question is, if in a synchronized block, a non volatile shared variable is being modified, will the other threads see the updated data ? (Since the variable in question is non volatile, other threads should read stale data from cache instead of main main memory)

If the answer to the above question is NO, then can I conclude that everytime I use synchronization, I should ensure that shared variables must be marked volatile ?

And if the answer is YES, then does that mean that I can always use synchronization instead of marking shared variables are volatile ?

p.s: Before asking this question, I have read through lot of answers on StackOverflow and on other sites, but I couldn't find the answer to my question.

Metronome answered 27/7, 2017 at 13:10 Comment(2)
@Sotirios: I am sure that my question is not a duplicate of this: #3215438 I was looking for something more than the difference between volatile and synchronization. Anyways thanks for the link, I will read through the answers.Metronome
What difference? You asked whether you need to mark a variable as volatile if you're only accessing it through a synchronized block. The answers in that question address that.Hypothesis
A
14

To simplify a little:

  • volatile only provides visibility: when you read a volatile variable you get two guarantees: (1) you see the latest write to the variable, even if it was performed in another thread and (2) all the writes before that volatile write are also visible.
  • synchronized gives you visibility AND atomicity - thread observing the actions performed in a synchronized block from a synchronized block using the same monitor will either see all of them or none of them.

So to answer your question, no, if a variable is written to within a synchronized block, you don't need to mark it volatile, provided that you always read that variable from a synchronized block using the same monitor.


Here are a few examples with volatile:

static class TestVolatile {
  private int i = 0;
  private volatile int v = 0;

  void write() {
    i = 5;
    v = 7;
  }

  void read() {
    //assuming write was called beforehand
    print(i); //could be 0 or 5
    print(v); //must be 7
    print(i); //must be 5
  }

  void increment() {
    i = i + 1; //if two threads call the method concurrently
               //i could be incremented by 1 only, not 2: no atomicity
  }
}

And a few examples with synchronized:

static class TestSynchronized {
  private int i = 0;
  private int j = 0;

  void write() {
    synchronized(this) {
      i = 5;
      j = 7;
    }
  }

  void read_OK() {
    synchronized(this) {
      //assuming write was called beforehand
      print(i); //must be 5
      print(j); //must be 7
      print(i); //must be 5
    }
  }

  void read_NOT_OK() {
    synchronized(new Object()) { //not the same monitor
      //assuming write was called beforehand
      print(i); //can be 0 or 5
      print(j); //can be 0 or 7
    }        
  }

  void increment() {
    synchronized(this) {
      i = i + 1; //atomicity guarantees that if two threads call the method
                 //concurrently, i will be incremented twice
    }
  }
}
Alehouse answered 27/7, 2017 at 13:17 Comment(3)
So from your answer I understand that a read in synchronization block always happens from the main memory. But one has to careful to ensure that a non volatile read happens within a synchronization block. And hence it would be wise to mark such shared variables volatile just in case tomorrow somebody performs a read from a non synchronization block.Metronome
@RahulDevMishra Not really - you generally have two options: (1) make the synchronization an internal implementation detail and don't leak the variables - that way external code can use your class without worrying or (2) document what external client need to do - see for example: docs.oracle.com/javase/8/docs/api/java/util/…Alehouse
Keep also in mind that if you synchronise certain blocks of the code, and then perform reading and writing of shared variable there, do make sure that they are ALL synchronised on the same object (monitor)! Or else you will have synchronised blocks of code, yes, but they will not work in unison, hence reads & writes to the shared variable may become random...Insufferable
R
6

The JLS defines a relation called "happens-before" on instructions in a program. A short version can be seen in the documentation of java.util.concurrent.

A write operation to a variable is seen by a read operation of the same variable, if the write "happens-before" the read.

Now, if both threads access that variable only inside a synchronization block, then the exit from the synchronization block guarantees that what happened in it "happens-before" anything that happens after the next lock of the same synchronization monitor.

So if thread A writes to variable x inside a synchronized block, and thread B reads from that x inside a synchronized block on the same monitor, then x doesn't need to be volatile - the write "happened before" the read and its result will be visible to thread B.

But if thread B reads the variable without synchronization, then even if thread A did it inside synchronization, there is no guarantee that the write "happens before", and the variable is unsafe - unless it's volatile.

So if you make sure all access - both read and write - is within synchronization blocks on the same monitor, then you can rely on the "happens before" relation to make your write visible.

Ringworm answered 27/7, 2017 at 13:25 Comment(1)
One thing to realize is that although you are correct from a documentation perspective, the reality is that memory-barriers are not per monitor. I.e. if Thread A leaves a synchronized block and then Thread B enters another synchronized block, then thread B will see the updates made by Thread A. There is no guarantee of order and they can overlap, but if it happens in that order then B will see the updates.Wicklow
L
0

If that variable is protected by the same monitor lock every time it's accessed then there is no need to make it volatile.

Synchronized blocks do two things: single thread access to the regions protected by a lock and visibility effects. The visibility effects mean that any changes made to that variable while being protected by that lock will be visible for any other thread which enters a region which uses it (the lock).

Longitudinal answered 27/7, 2017 at 13:19 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.