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;
}
Clifford
  • 88,407
  • 13
  • 85
  • 165
Hongbo
  • 1,107
  • 2
  • 11
  • 18

5 Answers5

17

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:

jtahlborn
  • 52,909
  • 5
  • 76
  • 118
  • 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`. – OrangeDog Jan 08 '11 at 13:08
  • 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 Jan 08 '11 at 13:09
  • @OrangeDog - maybe you will believe Brian Goetz, one of the architects in the java memory model changes. added links to his articles. – jtahlborn Jan 08 '11 at 13:54
  • Thank you. Why could you not provide those links to start with? However, diversions into implementation issues that were fixed 7 years ago are not very helpful. I have amended my answer to avoid a sloppy use of the word "synchronisation". – OrangeDog Jan 08 '11 at 15:42
  • 2
    @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). – jtahlborn Jan 08 '11 at 20:25
  • 1
    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 Jul 07 '16 at 10:48
  • 1
    @user2259824 - as i already stated, it's not possible based on the jdk 1.5+ memory semantics. – jtahlborn Jul 09 '16 at 23:36
8

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.

Gugussee
  • 1,673
  • 3
  • 16
  • 26
  • 1
    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 Jan 06 '11 at 13:21
  • 2
    @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. – Sergei Tachenov Jan 06 '11 at 13:40
  • 1
    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 Jan 06 '11 at 16:42
  • @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. – Farhan stands with Palestine Aug 20 '16 at 19:13
  • @ShirgillFarhanAnsari - no, calls to `set()` do not write to `m`. you would only be reading the `m` reference in order to call ,set()`. – jtahlborn Aug 20 '16 at 23:49
  • @jtahlborn: It has been raised here- http://stackoverflow.com/questions/39073702/volatile-reference-and-subsequently-setting-the-value-of-the-variable – Farhan stands with Palestine Aug 22 '16 at 08:55
  • @ShirgillFarhanAnsari - added answer to your other question – jtahlborn Aug 22 '16 at 14:07
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.

Mario F
  • 45,569
  • 6
  • 37
  • 38
  • 3
    this is not correct. you ignored the second point in wikipedia where java 5 made volatile affect non-volatile variables. – jtahlborn Jan 06 '11 at 13:02
  • @jtahlborn you are right, I've added the second point and reworded my own explanation a bit. – Mario F Jan 06 '11 at 13:14
  • 1
    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. – jtahlborn Jan 06 '11 at 13:26
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.

OrangeDog
  • 36,653
  • 12
  • 122
  • 207
  • 2
    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 Jan 06 '11 at 13:04
  • @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 Jan 06 '11 at 13:57
  • 1
    @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 Jan 06 '11 at 16:47
  • @jtahlborn - That describes a situation where only the object variable (m) is affected, and not its members. – OrangeDog Jan 06 '11 at 18:48
  • @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. – jtahlborn Jan 06 '11 at 20:21
  • What he is saying Orange, is that if you create a local variable Mutable t, invoke set with some arbitrary value and assign that local field t to m then yes the value of m will then read up to date. – John Vint Jan 06 '11 at 20:30
  • @John - Well, yes, obviously. That's due to `volatile`'s effect on the reference variable `m`, not to any effect it has on object members. – OrangeDog Jan 07 '11 at 00:16
  • Every line of code in the same thread happens-before the next one. Basic procedural semantics ensure `set()` happens-before `m = t`, and that the read of `m` happens-before `get()`. The `volatile` semantics only ensure that `m = t` happens-before the read of `m` iff the writing thread executed the write before the reading thread got to the read. – OrangeDog Jan 07 '11 at 00:24
  • @OrangeDog: no, that's not correct. @John's statement (while correct) was confusing because when he said "value", he meant the member field in Mutable. the happens-before semantics of volatiles ensure that _all_ actions by the thread taken before `m = t` are visible to any other thread which subsequently reads m, including any previous assignment to the "value" field. As i mentioned in another comment, you can see this in section 17 of the jls--volatile has the same memory semantics as synchronized. – jtahlborn Jan 07 '11 at 02:14
  • @jtahlborn - No, I am correct. What you are saying is a direct consequence of happens-before being a transient relation `(a < b & b < c -> a < c)`, as should be blindingly obvious given its name. This has nothing to do with the semantics of `volatile`. – OrangeDog Jan 07 '11 at 10:03
  • @OrangeDog: heh, that is one of the classic multi-threading pit-falls. i've seen this bug many times. the happens-before relationship is _not_ guaranteed _across_ 2 threads unless you have a "synchronization point" between the 2 threads. volatile provides that guarantee. please read up on this stuff before posting more misinformation. – jtahlborn Jan 07 '11 at 13:05
  • @jtahlborn - That is exactly what I have said. You are the one who is failing to separate the specific semantics of individual language features. Please read up on my comments before posting more misinformation. – OrangeDog Jan 07 '11 at 13:51
  • @OrangeDog: i've read all you comments and your original post. i'm sorry, you are still not correct. the semantics of volatile have a direct impact on the happens-before relationship between 2 threads. this is explicitly described in the latest jls, and was a _change_ between pre-jdk1.5 and jdk1.5+. i can back up what i am saying with the jls. can you back up your assertions? – jtahlborn Jan 07 '11 at 13:59
  • @OrangeDog Each line of code written by a developer (according to 1.5+ JMM) does not have to happen one after another in the same thread. It just appears that way. Sequential consistency is different then linearizability. When you run a program in which has no synchronization, code can be ordered however is legal. Happens-before relationships are only guaranteed, as jtahlborn, with the use of synchronization points. – John Vint Jan 07 '11 at 15:49
  • @John V. - "If x and y are actions of the same thread and x comes before y in program order, then hb(x, y)." - http://java.sun.com/docs/books/jls/third_edition/html/memory.html#64086 – OrangeDog Jan 07 '11 at 16:18
  • @jtahlborn - I'm not disagreeing with that, I'm disagreeing that what I have said is wrong. – OrangeDog Jan 07 '11 at 16:23
  • That is different then each line of code happens before the next one. – John Vint Jan 07 '11 at 16:25
  • @John V. - "happens before" or "happens-before"? That is a very important distinction. – OrangeDog Jan 07 '11 at 16:34
  • Even happens-before, the terminology specifically did not use 'each line of code happens-before the next line' because that isn't correct. A specific action appears to happen-before any subsequent action but makes no relation to the line of code in which they were executed on. – John Vint Jan 07 '11 at 16:40
  • @John V. - "x comes before y in program order" refers to the line of code the actions appear on in the program. "hb(x, y)" means x happens-before y. The execution order could be anything; this is a relationship between lines of code (program order) and happens-before. – OrangeDog Jan 07 '11 at 16:45
  • Lets use as an extreme exmaple. You have in a class final int j; final int i; public SoemConstructor(){ j = 10; i =10; } Nothing will stop the compiler to move the writes of j and i to be: final int j,i =10; Same line of code - lines of code for a good reason should not be used to define sequential consistency. – John Vint Jan 07 '11 at 16:55
  • @John V. - As your example shows (and I just said), happens-before is not the same as execution order. If instead the code was `i = 10 + f(j)`, then the compiler would not be able to permute the execution order without violating happens-before. (It could probably optimise it to `f(10)` though.) – OrangeDog Jan 07 '11 at 17:43
  • Yes that is right - The point I was making is program order isn't determined by lines of code, rather OS/JVM actions that take place. – John Vint Jan 07 '11 at 18:15
  • Added a separate answer below because i couldn't write the example in a comment. @OrangeDog: please comment on my answer and let me know what you disagree with. – jtahlborn Jan 07 '11 at 18:55
  • @John V. - "lines of code" == "program order" != "execution order" – OrangeDog Jan 08 '11 at 13:01
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.

Raedwald
  • 46,613
  • 43
  • 151
  • 237