Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using volatile keyword with mutable object

In Java, I understand that volatile keyword provides visibility to variables. The question is, if a variable is a reference to a mutable object, does volatile also provide visibility to the members inside that object?

In the example below, does it work correctly if multiple threads are accessing volatile Mutable m and changing the value?

example

class Mutable {     private int value;     public int get()     {         return a;     }     public int set(int value)     {         this.value = value;     } }  class Test {     public volatile Mutable m; } 
like image 748
Hongbo Avatar asked Jan 06 '11 11:01

Hongbo


People also ask

Can we use volatile for object in Java?

The volatile keyword can be used either with primitive type or objects. The volatile keyword does not cache the value of the variable and always read the variable from the main memory. The volatile keyword cannot be used with classes or methods.

Can volatile keyword used with instance variable?

Volatile can only be applied to instance variables, which are of type object or private.

What is volatile and mutable keywords?

mutable : The mutable keyword overrides any enclosing const statement. A mutable member of a const object can be modified. volatile : The volatile keyword is an implementation-dependent modifier, used when declaring variables, which prevents the compiler from optimizing those variables.

For what purpose would you use the keyword volatile?

The volatile keyword is intended to prevent the compiler from applying any optimizations on objects that can change in ways that cannot be determined by the compiler. Objects declared as volatile are omitted from optimization because their values can be changed by code outside the scope of current code at any time.


2 Answers

This is sort of a side note explanation on some of the details of volatile. Writing this here because it is too much for an comment. I want to give some examples which show how volatile affects visibility, and how that changed in jdk 1.5.

Given the following example code:

public class MyClass {   private int _n;   private volatile int _volN;    public void setN(int i) {     _n = i;   }   public void setVolN(int i) {     _volN = i;   }   public int getN() {      return _n;    }   public int getVolN() {      return _volN;    }    public static void main() {     final MyClass mc = new MyClass();      Thread t1 = new Thread() {       public void run() {         mc.setN(5);         mc.setVolN(5);       }     };      Thread t2 = new Thread() {       public void run() {         int volN = mc.getVolN();         int n = mc.getN();         System.out.println("Read: " + volN + ", " + n);       }     };      t1.start();     t2.start();   } } 

The behavior of this test code is well defined in jdk1.5+, but is not well defined pre-jdk1.5.

In the pre-jdk1.5 world, there was no defined relationship between volatile accesses and non-volatile accesses. therefore, the output of this program could be:

  1. Read: 0, 0
  2. Read: 0, 5
  3. Read: 5, 0
  4. Read: 5, 5

In the jdk1.5+ world, the semantics of volatile were changed so that volatile accesses affect non-volatile accesses in exactly the same way as synchronization. therefore, only certain outputs are possible in the jdk1.5+ world:

  1. Read: 0, 0
  2. Read: 0, 5
  3. Read: 5, 0 <- not possible
  4. Read: 5, 5

Output 3. is not possible because the reading of "5" from the volatile _volN establishes a synchronization point between the 2 threads, which means all actions from t1 taken before the assignment to _volN must be visible to t2.

Further reading:

  • Fixing the java memory model, part 1
  • Fixing the java memory model, part 2
like image 77
jtahlborn Avatar answered Oct 09 '22 22:10

jtahlborn


In your example the volatile keyword only guarantees that the last reference written, by any thread, to 'm' will be visible to any thread reading 'm' subsequently.

It doesn't guarantee anything about your get().

So using the following sequence:

Thread-1: get()     returns 2 Thread-2: set(3) Thread-1: get()     

it is totally legitimate for you to get back 2 and not 3. volatile doesn't change anything to that.

But if you change your Mutable class to this:

class Mutable {     private volatile int value;     public int get()     {         return a;     }     public int set(int value)     {         this.value = value;     } } 

Then it is guaranteed that the second get() from Thread-1 shall return 3.

Note however that volatile typically ain't the best synchronization method.

In you simple get/set example (I know it's just an example) a class like AtomicInteger, using proper synchronization and actually providing useful methods, would be better.

like image 31
Gugussee Avatar answered Oct 09 '22 20:10

Gugussee