Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 392
RDM Avatar asked Jul 27 '17 13:07

RDM


People also ask

Should all variables be volatile?

A variable should only be marked volatile if it is used both inside an ISR, and outside one. Variables only used outside an ISR should not be volatile. Variables only used inside an ISR should not be volatile.

Are volatile variables thread-safe Java?

Using volatile is yet another way (like synchronized, atomic wrapper) of making class thread-safe. Thread-safe means that a method or class instance can be used by multiple threads at the same time without any problem.

How volatile will work if we have multiple threads writing to a volatile variable?

If you write volatile variable from multiple threads without using any synchronized constructs, you are bound to get data inconsistency errors. Use volatile variables without synchronization in case of single write thread and multiple read threads for atomic operations.

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.


1 Answers

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
    }
  }
}
like image 71
assylias Avatar answered Oct 24 '22 07:10

assylias