Search code examples
javaconcurrencyvolatilemutable

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;
}

Solution

  • 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: