32

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;
}
2
  • 1
    For comparison, you might like to look at AtomicReference which does this corrctly and is builtin. Commented Jan 6, 2011 at 11:35
  • If you are only ever reading Test.m then there's no happens-before relationships set up at all. Commented Jan 6, 2011 at 13:38

5 Answers 5

18

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:

Sign up to request clarification or add additional context in comments.

7 Comments

OK, without volatile the following relation exists: hb(w_n, w_volN), hb(r_volN,r_n). If you add volatile, hb(w_volN,r_volN) could be added. By transitivity, it is now the case that hb(w_n,r_n). The transitivity rule is independent of the semantics of volatile.
I am unconvinced that these semantics are not also the case pre-1.5. They may have been implicit, but still would have to be well-defined.
@OrangeDog - They were the top hits on google, so they were pretty easy to find. My point was to show that volatile absolutely affects visibility. i was attempting to show how it changed as an example proving that it affects visibility. those links also indicate how volatile affects visibility. as such, the your answer is still at best misleading, at worst, wrong. (i actually was not concerned by your usage of the word synchronization, as that was the one correct statement in your answer).
Hey @jtahlborn, one follow up from future. Does case 3 being impossible imply that compiler or jvm cannot reorder assignment of independent variables if one of them is volatile? If t1 is reordered to assign volatile variable first than case 3 would be possible.
@user2259824 - as i already stated, it's not possible based on the jdk 1.5+ memory semantics.
|
9

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.

7 Comments

this answer is basically correct. one note however, when m is assigned, the internal value will be correctly visible. it is only after subsequent calls to set() which do not write m that you have problems.
@jtahlborn, could you provide a reference for that? I couldn't find it in JLS. It only says that "A write to a volatile field (§8.3.1.4) happens-before every subsequent read of that field", but that doesn't imply anything about the actual initialization of the object the field refers to.
read through section 17. the definition of "happens before" and 17.4.2 explain that volatile is now equivalent to synchronized in terms of memory semantics.
@jtahlborn: By internal value, you mean the properties of the instance. Since m is just a reference to the object, would it be wrong to say calls to set() actually writes to m.
@ShirgillFarhanAnsari - no, calls to set() do not write to m. you would only be reading the m reference in order to call ,set()`.
|
5

volatile only provides guarantees about the reference to the Object that is declared so. The members of that instance don't get synchronized.

According to the Wikipedia, you have:

  • (In all versions of Java) There is a global ordering on the reads and writes to a volatile variable. This implies that every thread accessing a volatile field will read its current value before continuing, instead of (potentially) using a cached value. (However, there is no guarantee about the relative ordering of volatile reads and writes with regular reads and writes, meaning that it's generally not a useful threading construct.)
  • (In Java 5 or later) Volatile reads and writes establish a happens-before relationship, much like acquiring and releasing a mutex.

So basically what you have is that by declaring the field volatile, interacting with it creates a "point of synchronization", after which any change will be visible in other threads. But after that, using get() or set() is unsynched. The Java Spec has a more thorough explanation.

3 Comments

this is not correct. you ignored the second point in wikipedia where java 5 made volatile affect non-volatile variables.
@jtahlborn you are right, I've added the second point and reworded my own explanation a bit.
your explanation is still not correct. it still seems to imply that volatile does not affect non-volatile fields. on the first assignment of m, the visibility of value is guaranteed. see my comment on @Gugusee's post for more details.
0

Use of volatile rather than a fully synchronized value is essentially an optimization. The optimization comes from the weaker guarantees provided for a volatile value compared with a synchronized access. Premature optimmization is the root of all evil; in this case, the evil could be hard to track down because it would be in the form of race conditions and such like. So if you need to ask, you probably ought not to use it.

Comments

0

volatile does not "provide visibility". Its only effect is to prevent processor caching of the variable, thus providing a happens-before relation on concurrent reads and writes. It does not affect the members of an object, nor does it provide any synchronisation synchronized locking.

As you haven't told us what the "correct" behaviour of your code is, the question cannot be answered.

25 Comments

Actually, volatile can affect the members of the object (not how the OP is using it, though). the happens before relationship does provide visibility guarantees.
@jtahlborn - Could you demonstrate how? On the second point; while the effect is that writes are visible to reads, the term "provides visibility to a variable" is too vague to be meaningful.
@OrangeDog: say you created a local instance of Mutable and called the set() method with 3 different values, then assigned your local Mutable instance to m. all subsequent reads of m would be guaranteed to see the "last" value set by the first thread before it assigned m. thus, the visibility of value is guaranteed when m is assigned. however, as correctly stated elsewhere, the affects of any future calls to set() after the assignment to m are not guaranteed to be visible. (where "visible" means guaranteed to be seen by another thread).
@jtahlborn - That describes a situation where only the object variable (m) is affected, and not its members.
@OrangeDog: the write to m is providing a visibility guarantee for value. maybe this seems logical to you, so you don't understand what i am saying. however, if you look at the volatile semantics pre-jdk1.5, you will see that this was not the case. pre-jdk1.5, value had absolutely no guarantees.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.